import { useState, useCallback, useRef } from 'react';
import type { FeatureFilters } from '../types';
import { apiUrl, authHeaders, logNonAbortError } from '../lib/api';

export interface AiTravelTimeFilter {
  /**
   * Server-side mode string. May be a base mode (car|bicycle|walking|transit)
   * or a transit variant (transit-no-bus, transit-no-change, …). Callers must
   * normalise via parseServerMode before constructing a TravelTimeEntry.
   */
  mode: string;
  slug: string;
  label: string;
  min?: number;
  max?: number;
}

export interface AiMatchBounds {
  south: number;
  west: number;
  north: number;
  east: number;
}

export interface AiFiltersResult {
  filters: FeatureFilters;
  travelTimeFilters: AiTravelTimeFilter[];
  notes: string;
  /** Human-readable summary of what was set */
  summary: string;
  /** Number of properties matching the proposed filters (excludes travel time) */
  matchCount: number;
  /** Bounding box of the matching properties, if any matched */
  matchBounds: AiMatchBounds | null;
}

export type AiFilterErrorType = 'auth' | 'limit' | 'error';

/** Context of currently active filters, sent for conversational refinement. */
export interface AiFiltersContext {
  filters: FeatureFilters;
  travelTime: { mode: string; label: string; min?: number; max?: number }[];
}

interface UseAiFiltersResult {
  fetchAiFilters: (query: string, context?: AiFiltersContext) => Promise<AiFiltersResult | null>;
  loading: boolean;
  error: string | null;
  errorType: AiFilterErrorType | null;
  notes: string | null;
  summary: string | null;
}

/** Build a human-readable summary of the AI result. */
function buildSummary(
  filters: FeatureFilters,
  travelTimeFilters: AiTravelTimeFilter[],
  matchCount: number
): string {
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const i18n = require('../i18n').default as {
    t: (key: string, opts?: Record<string, unknown>) => string;
  };
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const { ts } = require('../i18n/server') as { ts: (v: string) => string };
  const parts: string[] = [];

  for (const [name, value] of Object.entries(filters)) {
    if (Array.isArray(value) && value.length === 2 && typeof value[0] === 'number') {
      parts.push(ts(name));
    } else if (Array.isArray(value)) {
      parts.push(`${ts(name)}: ${(value as string[]).map((v) => ts(v)).join(', ')}`);
    }
  }

  for (const tt of travelTimeFilters) {
    const bounds =
      tt.max !== undefined
        ? i18n.t('format.lessThanMin', { max: tt.max })
        : tt.min !== undefined
          ? i18n.t('format.moreThanMin', { min: tt.min })
          : '';
    parts.push(i18n.t('format.toDestination', { mode: tt.mode, label: tt.label, bounds }).trim());
  }

  if (parts.length === 0) return i18n.t('format.noFiltersSet');
  return `${i18n.t('format.propertiesMatch', { count: matchCount.toLocaleString() })} \u00B7 ${i18n.t('format.setFilters', { count: parts.length, list: parts.join(', ') })}`;
}

export function useAiFilters(): UseAiFiltersResult {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [errorType, setErrorType] = useState<AiFilterErrorType | null>(null);
  const [notes, setNotes] = useState<string | null>(null);
  const [summary, setSummary] = useState<string | null>(null);
  const abortRef = useRef<AbortController | null>(null);

  const fetchAiFilters = useCallback(
    async (query: string, context?: AiFiltersContext): Promise<AiFiltersResult | null> => {
      abortRef.current?.abort();
      const controller = new AbortController();
      abortRef.current = controller;

      setLoading(true);
      setError(null);
      setErrorType(null);
      setNotes(null);
      setSummary(null);

      try {
        const url = apiUrl('ai-filters');
        const bodyObj: Record<string, unknown> = { query };
        if (context) {
          bodyObj.context = {
            filters: context.filters,
            travel_time: context.travelTime,
          };
        }
        const response = await fetch(
          url,
          authHeaders({
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(bodyObj),
            signal: controller.signal,
          })
        );

        if (!response.ok) {
          const text = await response.text();
          if (response.status === 401) {
            setErrorType('auth');
            setError(text || 'Login required');
          } else if (response.status === 429) {
            setErrorType('limit');
            setError(text || 'Weekly usage limit reached');
          } else {
            setErrorType('error');
            setError(text || `HTTP ${response.status}`);
          }
          setLoading(false);
          return null;
        }

        const json = await response.json();
        const travelTimeFilters: AiTravelTimeFilter[] = (json.travel_time_filters || []).map(
          (tt: { mode: string; slug: string; label: string; min?: number; max?: number }) => ({
            mode: tt.mode,
            slug: tt.slug,
            label: tt.label,
            min: tt.min,
            max: tt.max,
          })
        );
        const filters = json.filters as FeatureFilters;
        const matchCount: number = json.match_count ?? 0;
        const summaryText = buildSummary(filters, travelTimeFilters, matchCount);
        const result: AiFiltersResult = {
          filters,
          travelTimeFilters,
          notes: json.notes || '',
          summary: summaryText,
          matchCount,
          matchBounds: (json.match_bounds as AiMatchBounds | undefined) ?? null,
        };
        setNotes(result.notes || null);
        setSummary(summaryText);
        setLoading(false);
        return result;
      } catch (err) {
        if (controller.signal.aborted) return null;
        logNonAbortError('ai-filters', err);
        const message = err instanceof Error ? err.message : 'Failed to generate filters';
        setErrorType('error');
        setError(message);
        setLoading(false);
        return null;
      }
    },
    []
  );

  return { fetchAiFilters, loading, error, errorType, notes, summary };
}
