"use client";

import { keepPreviousData, useMutation, useQuery } from "@tanstack/react-query";

import { apiFetch } from "./api";
import type {
  AggregatedEstimate,
  CianPriceChangeStats,
  EstimateHistoryItem,
  GeocodeSuggestion,
  GeocodeSuggestResponse,
  HouseAnalyticsResponse,
  HouseInfoForEstimate,
  IMVBenchmarkResponse,
  LocationIndexResponse,
  PlacementHistoryItem,
  SalesVsListingsResponse,
  SellTimeSensitivityResponse,
  StreetDealsResponse,
  TradeInEstimateInput,
  TradeInLeadInput,
  TradeInLeadResponse,
} from "@/types/trade-in";

const BASE = "/api/v1/trade-in";
const GEOCODE_BASE = "/api/v1/geocode";

/**
 * POST /api/v1/trade-in/estimate
 * Sends apartment parameters and returns AggregatedEstimate (mock data for now).
 */
export function useEstimateMutation() {
  return useMutation<AggregatedEstimate, Error, TradeInEstimateInput>({
    mutationFn: (input) =>
      apiFetch<AggregatedEstimate>(`${BASE}/estimate`, {
        method: "POST",
        body: JSON.stringify(input),
      }),
  });
}

/**
 * POST /api/v1/trade-in/lead
 * Контактная заявка (issue #2377 / backend #2376, PR #2390) — телефон + явное
 * согласие ФЗ-152, опционально привязанные к оценке (estimate_id). 422 если
 * consent !== true, 404 если estimate_id не найден. Notification (Telegram/
 * email) вне scope — только persist в trade_in_leads.
 */
export function useCreateLeadMutation() {
  return useMutation<TradeInLeadResponse, Error, TradeInLeadInput>({
    mutationFn: (input) =>
      apiFetch<TradeInLeadResponse>(`${BASE}/lead`, {
        method: "POST",
        body: JSON.stringify(input),
      }),
  });
}

/**
 * GET /api/v1/trade-in/estimate/{id}
 * Fetches a previously computed estimate by UUID (for shareable links / PDF).
 */
export function useEstimate(estimate_id: string | null) {
  return useQuery<AggregatedEstimate>({
    queryKey: ["trade-in", "estimate", estimate_id],
    queryFn: () =>
      apiFetch<AggregatedEstimate>(`${BASE}/estimate/${estimate_id}`),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000, // 10 min — estimates expire at expires_at anyway
  });
}

/**
 * GET /api/v1/trade-in/estimate/{id}/houses
 * Houses в радиусе 500m от target адреса estimate (Stage 2c data).
 */
export function useEstimateHouses(estimate_id: string | null) {
  return useQuery<HouseInfoForEstimate[]>({
    queryKey: ["trade-in", "estimate", estimate_id, "houses"],
    queryFn: () =>
      apiFetch<HouseInfoForEstimate[]>(`${BASE}/estimate/${estimate_id}/houses`),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/estimate/{id}/placement-history
 * Historical listings from house_placement_history for the target house.
 */
export function useEstimatePlacementHistory(estimate_id: string | null) {
  return useQuery<PlacementHistoryItem[]>({
    queryKey: ["trade-in", "estimate", estimate_id, "placement-history"],
    queryFn: () =>
      apiFetch<PlacementHistoryItem[]>(
        `${BASE}/estimate/${estimate_id}/placement-history`,
      ),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/estimate/{id}/imv-benchmark
 * Avito IMV benchmark для UI badge (Stage 3 IMV cache lookup).
 */
export function useEstimateImvBenchmark(estimate_id: string | null) {
  return useQuery<IMVBenchmarkResponse>({
    queryKey: ["trade-in", "estimate", estimate_id, "imv-benchmark"],
    queryFn: () =>
      apiFetch<IMVBenchmarkResponse>(`${BASE}/estimate/${estimate_id}/imv-benchmark`),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/estimate/{id}/cian-price-changes
 * Price change history for Cian analog listings (only those with >0 changes).
 */
export function useEstimateCianPriceChanges(estimate_id: string | null) {
  return useQuery<CianPriceChangeStats[]>({
    queryKey: ["trade-in", "estimate", estimate_id, "cian-price-changes"],
    queryFn: () =>
      apiFetch<CianPriceChangeStats[]>(
        `${BASE}/estimate/${estimate_id}/cian-price-changes`,
      ),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/estimate/{id}/house-analytics
 * Price history, KPI and recent sold lots for the target house.
 */
export function useEstimateHouseAnalytics(estimate_id: string | null) {
  return useQuery<HouseAnalyticsResponse>({
    queryKey: ["trade-in", "estimate", estimate_id, "house-analytics"],
    queryFn: () =>
      apiFetch<HouseAnalyticsResponse>(
        `${BASE}/estimate/${estimate_id}/house-analytics`,
      ),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/location-index?estimate_id=&radius_m=
 * Location index for the estimate's target address — replaces the broken
 * location-coef (backend rewrite, see app/services/location_index.py):
 * % deviation of the local median ₽/м² (comparable active listings near the
 * address) from the citywide median ₽/м², NOT a price multiplier and NOT fed
 * into the estimate. status="out_of_coverage"/"insufficient_data" are honest
 * graceful-fallback responses (address outside Yekaterinburg / too few
 * comparables) — ./v2/mappers.ts renders each distinctly, never a fabricated
 * number.
 */
export function useLocationIndex(estimate_id: string | null, radius_m?: number) {
  const params = new URLSearchParams();
  if (estimate_id) params.set("estimate_id", estimate_id);
  if (radius_m != null) params.set("radius_m", String(radius_m));
  return useQuery<LocationIndexResponse>({
    queryKey: ["trade-in", "location-index", estimate_id, radius_m ?? null],
    queryFn: () =>
      apiFetch<LocationIndexResponse>(`${BASE}/location-index?${params}`),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/street-deals
 * ДКП-сделки Росреестра по улице target адреса. Per-street (open dataset
 * без номера дома). Empty response если street не извлёкся.
 */
export function useStreetDeals(
  address: string | null,
  area_m2: number | null,
  rooms: number | null,
) {
  const params = new URLSearchParams();
  if (address) params.set("address", address);
  if (area_m2 !== null) params.set("area_m2", String(area_m2));
  if (rooms !== null) params.set("rooms", String(rooms));
  return useQuery<StreetDealsResponse>({
    queryKey: ["trade-in", "street-deals", address, area_m2, rooms],
    queryFn: () =>
      apiFetch<StreetDealsResponse>(`${BASE}/street-deals?${params}`),
    enabled: !!address && area_m2 !== null && rooms !== null,
    staleTime: 5 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/sales-vs-listings
 * ДКП-сделки + paired listings по улице (PR L / #564 Phase 2). Возвращает все
 * пары LEFT JOIN — сделки без listing match сохраняются (listing_* = null),
 * чтобы вычислить linkage_rate. Используется в StreetDealsCard для показа
 * исторических ASK рядом с фактической ценой сделки.
 *
 * staleTime: 10 минут — данные обновляются раз в сутки (rosreestr import +
 * scrapers), нет смысла часто рефетчить.
 */
export function useSalesVsListings(
  address: string | null,
  area_m2: number | null,
  rooms: number | null,
) {
  const params = new URLSearchParams();
  if (address) params.set("address", address);
  if (area_m2 !== null) params.set("area_m2", String(area_m2));
  if (rooms !== null) params.set("rooms", String(rooms));
  return useQuery<SalesVsListingsResponse>({
    queryKey: ["trade-in", "sales-vs-listings", address, area_m2, rooms],
    queryFn: () =>
      apiFetch<SalesVsListingsResponse>(`${BASE}/sales-vs-listings?${params}`),
    enabled: !!address && area_m2 !== null && rooms !== null,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/estimate/{id}/sell-time-sensitivity
 * Median exposure days bucketed by price premium (-5%, 0, +5%, +10%).
 */
export function useEstimateSellTimeSensitivity(estimate_id: string | null) {
  return useQuery<SellTimeSensitivityResponse>({
    queryKey: ["trade-in", "estimate", estimate_id, "sell-time-sensitivity"],
    queryFn: () =>
      apiFetch<SellTimeSensitivityResponse>(
        `${BASE}/estimate/${estimate_id}/sell-time-sensitivity`,
      ),
    enabled: estimate_id !== null && estimate_id.length > 0,
    staleTime: 10 * 60_000,
  });
}

/**
 * GET /api/v1/trade-in/history?limit=N
 * Per-user список последних оценок (CacheView · «Предыдущие оценки»). Скоупится
 * по X-Authenticated-User в бэке (#656) — header проставляет Caddy basic_auth.
 *
 * Fail-soft как useQuota: `retry:false` чтобы 401 на протухшей сессии не
 * ре-стучался. Освежать после успешной оценки явным invalidateQueries(
 * ["trade-in","history"]).
 */
export function useEstimateHistory(limit = 50) {
  return useQuery<EstimateHistoryItem[]>({
    queryKey: ["trade-in", "history", limit],
    queryFn: () =>
      apiFetch<EstimateHistoryItem[]>(`${BASE}/history?limit=${limit}`),
    staleTime: 30_000,
    retry: false,
  });
}

/**
 * GET /api/v1/geocode/suggest?q=&limit=
 * Автокомплит адресов в пределах ЕКБ для поля адреса (ParamsPanel). Debounce-
 * friendly: вызывающий компонент дебаунсит строку query, хук кешируется по
 * queryKey; `enabled` срабатывает только начиная с 3 символов (бэкенд min 2,
 * берём 3 чтобы не дёргать на 1-2 символа). `select` разворачивает обёртку
 * {items} → GeocodeSuggestion[]; keepPreviousData убирает мерцание списка между
 * последовательными запросами.
 */
export function useGeocodeSuggest(query: string, limit = 8) {
  const q = query.trim();
  return useQuery<GeocodeSuggestResponse, Error, GeocodeSuggestion[]>({
    queryKey: ["trade-in", "geocode-suggest", q, limit],
    queryFn: () =>
      apiFetch<GeocodeSuggestResponse>(
        `${GEOCODE_BASE}/suggest?q=${encodeURIComponent(q)}&limit=${limit}`,
      ),
    select: (r) => r.items,
    enabled: q.length >= 3,
    staleTime: 5 * 60_000,
    retry: false,
    placeholderData: keepPreviousData,
  });
}
