const API_BASE =
  (typeof window !== "undefined" &&
    (window as Window & { __RESTO__?: { apiBase?: string } }).__RESTO__
      ?.apiBase) ||
  import.meta.env.VITE_API_BASE ||
  "/resto/api";

/** Путь со слешом в конце — под Bitrix/nginx; запрос всегда через index.php?route= */
function apiUrl(path: string): string {
  const qIndex = path.indexOf("?");
  const pathname = qIndex >= 0 ? path.slice(0, qIndex) : path;
  const query = qIndex >= 0 ? path.slice(qIndex + 1) : "";
  const route = pathname.endsWith("/") ? pathname : `${pathname}/`;
  const params = new URLSearchParams(query);
  params.set("route", route);
  return `${API_BASE}/index.php?${params.toString()}`;
}

async function request<T>(path: string, init?: RequestInit): Promise<T> {
  const response = await fetch(apiUrl(path), {
    headers: { "Content-Type": "application/json", ...(init?.headers ?? {}) },
    ...init,
  });
  const data = await response.json();
  if (!response.ok) {
    throw new Error(data.error ?? "API error");
  }
  return data as T;
}

export type FacetOption = { value: string; label: string };

export type Restaurant = {
  resto_id: number;
  title: string;
  address: string;
  phone: string;
  cuisine: string;
  price_range: string;
  metro: string;
  types: string;
  ring: string;
  district: string;
  okrug: string;
  preorder: string;
  chef_name: string;
  children_amenities: string;
  has_photos: boolean;
  photos_count: number;
  logo_url: string;
  booking_available: boolean;
  resto_updated: string;
  zon_id: string;
  mapped_bitrix_id: number | null;
  send_booking?: "auto" | "off";
  effective_send_booking?: boolean;
};

export type RestaurantDetail = Restaurant & {
  description?: string;
  entertainment?: string;
  delivery_options?: string;
  inside_features?: string;
  menu_types?: string;
  offers?: string;
  worktime_summary?: string;
  mapped_bitrix_name?: string | null;
  lat?: string;
  long?: string;
  photo_urls?: string[];
  panorama_url?: string;
};

export type BitrixCandidate = {
  bitrix_id: number;
  name: string;
  code: string;
  address: string;
  coordinates: string;
  id_resto: string;
  id_zon: string;
  active: string;
  match_score: number;
  mapped_resto_id?: number | null;
  is_mapped?: boolean;
};

export type MatchSuggestion = {
  id: number;
  kind: "resto" | "bitrix";
  resto_id: number | null;
  bitrix_id: number | null;
  suggested_bitrix_id: number | null;
  suggested_resto_id: number | null;
  score: number;
  reason: string | null;
  status: "high" | "suggested" | "weak" | "none";
  resto_title: string | null;
  resto_address: string | null;
  bitrix_name: string | null;
  bitrix_address: string | null;
  run_at: string;
};

export type MatchingStatus = {
  run_at: string | null;
  totals: Record<string, number>;
  unmapped: { resto: number; bitrix: number };
};

export type Filters = {
  q: string;
  type: string;
  cuisine: string;
  price_range: string;
  metro: string;
  district: string;
  okrug: string;
  ring: string;
  preorder: string;
  chef: string;
  children: string;
  entertainment: string;
  delivery: string;
  inside: string;
  menu: string;
  offers: string;
  booking_available: string;
  has_photos: string;
  mapped: string;
  send_booking: string;
  effective_send: string;
};

export const emptyFilters = (): Filters => ({
  q: "",
  type: "",
  cuisine: "",
  price_range: "",
  metro: "",
  district: "",
  okrug: "",
  ring: "",
  preorder: "",
  chef: "",
  children: "",
  entertainment: "",
  delivery: "",
  inside: "",
  menu: "",
  offers: "",
  booking_available: "",
  has_photos: "",
  mapped: "",
  send_booking: "",
  effective_send: "",
});

export type FeedExportPayload = {
  schema_version: number;
  exported_at: string;
  filters: Partial<Filters>;
  total: number;
  items: RestaurantDetail[];
};

export type FeedExportResult = {
  filename: string;
  saved_to: string;
  exported_at: string;
  total: number;
  payload: FeedExportPayload;
};

export function downloadJsonFile(filename: string, data: unknown): void {
  const blob = new Blob([JSON.stringify(data, null, 2)], {
    type: "application/json;charset=utf-8",
  });
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = filename;
  link.click();
  URL.revokeObjectURL(url);
}

export const api = {
  stats: () => request<{ stats: Record<string, number> }>("/stats"),
  facets: () => request<{ facets: Record<string, FacetOption[]> }>("/filters"),
  restaurants: (params: Record<string, string | number>) => {
    const query = new URLSearchParams();
    Object.entries(params).forEach(([key, value]) => {
      if (value !== "" && value !== undefined && value !== null) {
        query.set(key, String(value));
      }
    });
    return request<{
      items: Restaurant[];
      total: number;
      mapped_total: number;
      page: number;
      per_page: number;
      pages: number;
    }>(`/restaurants?${query.toString()}`);
  },
  restaurant: (id: number) =>
    request<{ item: RestaurantDetail }>(`/restaurants/${id}`),
  searchBitrix: (q: string, restoId?: number) => {
    const params = new URLSearchParams({ q });
    if (restoId) params.set("resto_id", String(restoId));
    return request<{ items: BitrixCandidate[] }>(
      `/bitrix/search?${params.toString()}`,
    );
  },
  createMapping: (restoId: number, bitrixId: number) =>
    request<{
      mapping: { resto_id: number; bitrix_id: number; bitrix_name: string };
    }>("/mapping", {
      method: "POST",
      body: JSON.stringify({ resto_id: restoId, bitrix_id: bitrixId }),
    }),
  deleteMapping: (restoId: number) =>
    request<{ mapping: { resto_id: number; bitrix_id: number } }>("/mapping", {
      method: "DELETE",
      body: JSON.stringify({ resto_id: restoId }),
    }),
  updateSendBooking: (restoId: number, sendBooking: "auto" | "off") =>
    request<{
      resto_id: number;
      send_booking: "auto" | "off";
      updated: number;
    }>("/mapping/send-booking", {
      method: "PATCH",
      body: JSON.stringify({ resto_id: restoId, send_booking: sendBooking }),
    }),
  bulkUpdateSendBooking: (restoIds: number[], sendBooking: "auto" | "off") =>
    request<{ updated: number; send_booking: "auto" | "off" }>(
      "/mapping/send-booking/bulk",
      {
        method: "POST",
        body: JSON.stringify({
          resto_ids: restoIds,
          send_booking: sendBooking,
        }),
      },
    ),
  updateSendBookingByFilter: (filters: Filters, sendBooking: "auto" | "off") =>
    request<{ updated: number; send_booking: "auto" | "off" }>(
      "/mapping/send-booking/by-filter",
      {
        method: "POST",
        body: JSON.stringify({ ...filters, send_booking: sendBooking }),
      },
    ),
  matchingStatus: () => request<MatchingStatus>("/matching/status"),
  runMatching: () =>
    request<{ run_at: string; totals: Record<string, number> }>(
      "/matching/run",
      { method: "POST" },
    ),
  matchingSuggestions: (params: Record<string, string | number>) => {
    const query = new URLSearchParams();
    Object.entries(params).forEach(([key, value]) => {
      if (value !== "" && value !== undefined && value !== null) {
        query.set(key, String(value));
      }
    });
    return request<{
      items: MatchSuggestion[];
      total: number;
      page: number;
      per_page: number;
      pages: number;
    }>(`/matching/suggestions?${query.toString()}`);
  },
  dismissSuggestion: (id: number) =>
    request<{ ok: boolean }>(`/matching/suggestions/${id}`, {
      method: "DELETE",
    }),
  exportJson: (filters: Filters) =>
    request<{ export: FeedExportResult }>("/export/json", {
      method: "POST",
      body: JSON.stringify(filters),
    }),
};

export type Booking = {
  id: number;
  created_at: string;
  bitrix_element_id: number | null;
  resto_id: number | null;
  zon_place_id: string | null;
  restaurant_name: string | null;
  restaurant_url: string | null;
  page_url: string | null;
  booking_type: string | null;
  persons: string | null;
  guest_name: string | null;
  guest_phone: string | null;
  comment: string | null;
  booking_date: string | null;
  booking_time: string | null;
  special_text: string | null;
  is_resto_linked: boolean;
  send_resto_enabled: boolean;
  zon_attempted: boolean;
  zon_ok: boolean;
  zon_codeanswer: string | null;
  zon_data: string | null;
  zon_response_raw: string | null;
  telegram_ok: boolean;
  telegram_error: string | null;
  retry_count: number;
  last_retry_at: string | null;
};

export type BookingStats = {
  total: number;
  linked: number;
  sent_to_zon: number;
  zon_failed: number;
  telegram_only: number;
  today: number;
};

export type BookingFilters = {
  q: string;
  linked: '' | '1' | '0';
  zon_status: '' | 'ok' | 'failed' | 'not_sent';
  date_from: string;
  date_to: string;
};

export const emptyBookingFilters = (): BookingFilters => ({
  q: '',
  linked: '',
  zon_status: '',
  date_from: '',
  date_to: '',
});

export type RetryZonResult = {
  id: number;
  attempted: boolean;
  ok: boolean;
  message: string;
  booking?: Booking;
};

export const bookingApi = {
  list: (params: Record<string, string | number>) => {
    const query = new URLSearchParams();
    Object.entries(params).forEach(([key, value]) => {
      if (value !== '' && value !== undefined && value !== null) {
        query.set(key, String(value));
      }
    });
    return request<{
      items: Booking[];
      total: number;
      page: number;
      per_page: number;
      pages: number;
    }>(`/bookings?${query.toString()}`);
  },
  stats: () => request<{ stats: BookingStats }>('/bookings/stats'),
  getById: (id: number) => request<{ item: Booking }>(`/bookings/${id}`),
  retryZon: (id: number) =>
    request<RetryZonResult>(`/bookings/${id}/retry-zon`, { method: 'POST' }),
};

export type SyncJob = {
  id: number;
  job_type: string;
  status: string;
  result: Record<string, unknown> | null;
  started_at: string;
  finished_at: string | null;
  error: string | null;
};

export type BitrixPropertySpec = {
  code: string;
  name: string;
  type: string;
  purpose: string;
};

export type SyncStatus = {
  stats: Record<string, number>;
  cache_files: number;
  bitrix_csv_mtime: string | null;
  resto_analysis: {
    api_total: number | null;
    cache_total: number;
    db_total?: number;
    missing_in_cache: number | null;
    extra_in_cache?: number;
    error?: string;
  };
  last_jobs: Record<string, SyncJob | null>;
  cron_recommended: { full_cache_refresh: boolean; note: string };
  bitrix_properties: BitrixPropertySpec[];
};

export type SyncJobResult = {
  job_id: number;
  job_type: string;
  status: string;
  result: Record<string, unknown>;
};

export const syncApi = {
  status: () => request<SyncStatus>("/sync/status"),
  bitrixImport: () =>
    request<SyncJobResult>("/sync/bitrix-import", { method: "POST" }),
  restoUpdate: () =>
    request<SyncJobResult>("/sync/resto-update", { method: "POST" }),
  restoCacheNew: (limit = 100) =>
    request<SyncJobResult>("/sync/resto-cache-new", {
      method: "POST",
      body: JSON.stringify({ limit }),
    }),
  restoAnalyze: () =>
    request<SyncJobResult>("/sync/resto-analyze", { method: "POST" }),
  bitrixPush: (dryRun = false) =>
    request<SyncJobResult>("/sync/bitrix-push", {
      method: "POST",
      body: JSON.stringify({ dry_run: dryRun }),
    }),
};
