import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from "react";
import type { CfAccount, CfZone, DnsRecord } from "../types";
import {
  apiVerify,
  cfRequest,
  fetchAccountsApi,
  migrateLegacyAccountsIfNeeded,
} from "../api/client";
import { STORAGE_ACTIVE_ACCOUNT, STORAGE_APP_TOKEN } from "../constants";

export type ToastState = { text: string; isError: boolean } | null;

type DomainflareContextValue = {
  appToken: string;
  unlocked: boolean;
  sessionBootstrapDone: boolean;
  unlockWithToken: (token: string) => Promise<void>;
  toast: (msg: string, isError?: boolean) => void;
  toastState: ToastState;
  accounts: CfAccount[];
  activeAccountId: string | null;
  selectAccount: (id: string) => void;
  refreshAccounts: () => Promise<void>;
  addAccount: (name: string, token: string) => Promise<void>;
  deleteAccount: (account: CfAccount) => Promise<void>;
  zones: CfZone[];
  zonesFilter: string;
  setZonesFilter: (v: string) => void;
  zonesPage: number;
  zonesTotalPages: number;
  zonesPageInfo: string;
  goZonesPrev: () => void;
  goZonesNext: () => void;
  refreshZonesList: () => void;
  activeZoneId: string | null;
  selectZone: (z: CfZone) => void;
  filteredZones: CfZone[];
  recordsPage: number;
  recordsTotalPages: number;
  recordsPageInfo: string;
  goRecordsPrev: () => void;
  goRecordsNext: () => void;
  fltType: string;
  setFltType: (v: string) => void;
  fltName: string;
  setFltName: (v: string) => void;
  fltContent: string;
  setFltContent: (v: string) => void;
  recordsStatus: string;
  records: DnsRecord[];
  searchRecords: () => Promise<void>;
  cf: (path: string, init?: RequestInit) => Promise<unknown>;
  activeZone: CfZone | null;
};

const DomainflareContext = createContext<DomainflareContextValue | null>(null);

export function useDomainflare(): DomainflareContextValue {
  const v = useContext(DomainflareContext);
  if (!v) throw new Error("useDomainflare 须在 Provider 内使用");
  return v;
}

export function DomainflareProvider({ children }: { children: ReactNode }) {
  const [appToken, setAppToken] = useState("");
  const [unlocked, setUnlocked] = useState(false);
  const [sessionBootstrapDone, setSessionBootstrapDone] = useState(() => {
    try {
      const t =
        sessionStorage.getItem(STORAGE_APP_TOKEN) || localStorage.getItem(STORAGE_APP_TOKEN);
      return !t;
    } catch {
      return true;
    }
  });
  const [toastState, setToastState] = useState<ToastState>(null);
  const [accounts, setAccounts] = useState<CfAccount[]>([]);
  const [activeAccountId, setActiveAccountId] = useState<string | null>(null);
  const [zones, setZones] = useState<CfZone[]>([]);
  const [zonesPage, setZonesPage] = useState(1);
  const [zonesTotalPages, setZonesTotalPages] = useState(1);
  const [zonesPageInfo, setZonesPageInfo] = useState("");
  const [zonesFilter, setZonesFilter] = useState("");
  const [activeZoneId, setActiveZoneId] = useState<string | null>(null);
  const [recordsPage, setRecordsPage] = useState(1);
  const [recordsTotalPages, setRecordsTotalPages] = useState(1);
  const [recordsPageInfo, setRecordsPageInfo] = useState("");
  const [perPage] = useState(50);
  const [fltType, setFltType] = useState("");
  const [fltName, setFltName] = useState("");
  const [fltContent, setFltContent] = useState("");
  const [recordsStatus, setRecordsStatus] = useState("");
  const [records, setRecords] = useState<DnsRecord[]>([]);

  /** 解析记录筛选：仅用 ref，避免输入时触发自动请求（与原版「查询」按钮一致） */
  const recordFiltersRef = useRef({ fltType: "", fltName: "", fltContent: "" });
  useEffect(() => {
    recordFiltersRef.current = { fltType, fltName, fltContent };
  }, [fltType, fltName, fltContent]);

  const toast = useCallback((msg: string, isError?: boolean) => {
    setToastState({ text: msg, isError: !!isError });
  }, []);

  useEffect(() => {
    if (!toastState) return;
    const t = window.setTimeout(() => setToastState(null), 3200);
    return () => window.clearTimeout(t);
  }, [toastState]);

  const activeAccount = useMemo(
    () => accounts.find((a) => a.id === activeAccountId) ?? null,
    [accounts, activeAccountId],
  );

  const cf = useCallback(
    async (path: string, init?: RequestInit) => {
      if (!activeAccount) throw new Error("未选择账号");
      return cfRequest(appToken, activeAccount.token, path, init);
    },
    [appToken, activeAccount],
  );

  /** 从服务端同步账号列表；若有迁移 legacy 返回 true */
  const syncAccountsFromServer = useCallback(
    async (panelToken: string): Promise<boolean> => {
      let list = await fetchAccountsApi(panelToken);
      let migrated = false;
      if (!list.length) {
        migrated = await migrateLegacyAccountsIfNeeded(panelToken);
        list = await fetchAccountsApi(panelToken);
      }
      setAccounts(list);
      const saved = sessionStorage.getItem(STORAGE_ACTIVE_ACCOUNT);
      let nextActive: string | null = null;
      if (saved && list.some((a) => a.id === saved)) nextActive = saved;
      else nextActive = list[0]?.id ?? null;
      setActiveAccountId(nextActive);
      if (nextActive) sessionStorage.setItem(STORAGE_ACTIVE_ACCOUNT, nextActive);
      return migrated;
    },
    [],
  );

  const bootstrapAfterAuth = useCallback(
    async (panelToken: string) => {
      const migrated = await syncAccountsFromServer(panelToken);
      if (migrated) toast("已将浏览器中的旧账号迁入 D1");
      setZonesPage(1);
      setActiveZoneId(null);
      setRecords([]);
      setRecordsPage(1);
      setRecordsPageInfo("");
      setZonesPageInfo("");
    },
    [syncAccountsFromServer, toast],
  );

  useEffect(() => {
    const t =
      sessionStorage.getItem(STORAGE_APP_TOKEN) || localStorage.getItem(STORAGE_APP_TOKEN);
    if (!t) return;

    void (async () => {
      try {
        if (!(await apiVerify(t))) return;
        setAppToken(t);
        setUnlocked(true);
        try {
          await bootstrapAfterAuth(t);
        } catch (e) {
          toast(e instanceof Error ? e.message : String(e), true);
        }
      } finally {
        setSessionBootstrapDone(true);
      }
    })();
  }, [bootstrapAfterAuth, toast]);

  const unlockWithToken = useCallback(
    async (token: string) => {
      setSessionBootstrapDone(false);
      try {
        if (!(await apiVerify(token))) {
          throw new Error("令牌不正确");
        }
        setAppToken(token);
        sessionStorage.setItem(STORAGE_APP_TOKEN, token);
        localStorage.setItem(STORAGE_APP_TOKEN, token);
        setUnlocked(true);
        await bootstrapAfterAuth(token);
      } finally {
        setSessionBootstrapDone(true);
      }
    },
    [bootstrapAfterAuth],
  );

  const refreshAccounts = useCallback(async () => {
    const migrated = await syncAccountsFromServer(appToken);
    if (migrated) toast("已将浏览器中的旧账号迁入 D1");
  }, [appToken, syncAccountsFromServer, toast]);

  const selectAccount = useCallback((id: string) => {
    setActiveAccountId(id);
    sessionStorage.setItem(STORAGE_ACTIVE_ACCOUNT, id);
    setZones([]);
    setZonesPageInfo("");
    setActiveZoneId(null);
    setRecords([]);
    setRecordsStatus("");
    setRecordsPageInfo("");
    setZonesPage(1);
  }, []);

  const fetchZonesForPage = useCallback(
    async (page: number) => {
      if (!activeAccountId || !activeAccount) {
        setZones([]);
        setZonesPageInfo("");
        return;
      }
      setRecordsStatus("加载域名列表…");
      try {
        const qp = new URLSearchParams();
        qp.set("page", String(page));
        qp.set("per_page", "50");
        qp.set("direction", "desc");
        const json = (await cfRequest(
          appToken,
          activeAccount.token,
          `/zones?${qp}`,
        )) as {
          result?: CfZone[];
          result_info?: { page?: number; total_pages?: number };
        };
        setZones(json.result ?? []);
        const zi = json.result_info;
        const totalP = Math.max(1, zi?.total_pages ?? 1);
        setZonesTotalPages(totalP);
        setZonesPage(page);
        setZonesPageInfo(zi ? `第 ${zi.page} / ${zi.total_pages} 页` : "");
        setRecordsStatus(activeZoneId ? "" : "请选择域名");
      } catch (e) {
        const msg = e instanceof Error ? e.message : String(e);
        setRecordsStatus(msg);
        toast(msg, true);
        setZonesPageInfo("");
      }
    },
    [activeAccount, activeAccountId, activeZoneId, appToken, toast],
  );

  useEffect(() => {
    if (!unlocked || !activeAccountId || !activeAccount) {
      setZones([]);
      return;
    }
    void fetchZonesForPage(zonesPage);
  }, [activeAccount, activeAccountId, fetchZonesForPage, unlocked, zonesPage]);

  const fetchRecordsPage = useCallback(
    async (page: number) => {
      if (!activeZoneId || !activeAccount) return;
      const zid = activeZoneId;
      const { fltType: ft, fltName: fn, fltContent: fc } = recordFiltersRef.current;
      const qs = new URLSearchParams();
      qs.set("page", String(page));
      qs.set("per_page", String(perPage));
      if (ft) qs.set("type", ft);
      if (fn.trim()) qs.set("name", fn.trim());
      if (fc.trim()) qs.set("content", fc.trim());
      setRecordsStatus("加载解析记录…");
      try {
        const json = (await cfRequest(
          appToken,
          activeAccount.token,
          `/zones/${zid}/dns_records?${qs}`,
        )) as {
          result?: DnsRecord[];
          result_info?: { total_count?: number; page?: number; total_pages?: number };
        };
        const rows = json.result ?? [];
        const info = json.result_info;
        setRecords(rows);
        const tp = Math.max(1, info?.total_pages ?? 1);
        setRecordsTotalPages(tp);
        setRecordsPage(page);
        setRecordsPageInfo(info ? `第 ${info.page} / ${info.total_pages} 页` : "");
        setRecordsStatus(`共 ${info?.total_count ?? rows.length} 条`);
      } catch (e) {
        const msg = e instanceof Error ? e.message : String(e);
        setRecordsStatus(msg);
        toast(msg, true);
        setRecordsPageInfo("");
      }
    },
    [activeAccount, activeZoneId, appToken, perPage, toast],
  );

  useEffect(() => {
    if (!activeZoneId || !activeAccount) return;
    void fetchRecordsPage(recordsPage);
  }, [activeAccount, activeZoneId, fetchRecordsPage, recordsPage]);

  const filteredZones = useMemo(() => {
    const q = zonesFilter.trim().toLowerCase();
    return zones.filter((z) => !q || (z.name || "").toLowerCase().includes(q));
  }, [zones, zonesFilter]);

  const activeZone = useMemo(
    () => zones.find((z) => z.id === activeZoneId) ?? null,
    [zones, activeZoneId],
  );

  const selectZone = useCallback((z: CfZone) => {
    setActiveZoneId(z.id);
    setRecordsPage(1);
    setRecordsPageInfo("");
  }, []);

  const addAccount = useCallback(
    async (name: string, token: string) => {
      const r = await fetch("/api/accounts", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-App-Token": appToken,
        },
        body: JSON.stringify({ name: name || "账号", token }),
      });
      const text = await r.text();
      if (!r.ok) {
        let msg = text;
        try {
          msg = (JSON.parse(text) as { errors?: { message: string }[] }).errors?.[0]?.message || text;
        } catch {
          /* keep */
        }
        throw new Error(msg);
      }
      await refreshAccounts();
      await fetchZonesForPage(1);
      toast("已写入 D1");
    },
    [appToken, fetchZonesForPage, refreshAccounts, toast],
  );

  const deleteAccount = useCallback(
    async (account: CfAccount) => {
      if (!confirm(`删除账号「${account.name}」？（将自数据库移除）`)) return;
      const r = await fetch(`/api/accounts/${encodeURIComponent(account.id)}`, {
        method: "DELETE",
        headers: { "X-App-Token": appToken },
      });
      const text = await r.text();
      if (!r.ok) {
        let msg = text;
        try {
          msg = (JSON.parse(text) as { errors?: { message: string }[] }).errors?.[0]?.message || text;
        } catch {
          /* keep */
        }
        throw new Error(msg);
      }
      await refreshAccounts();
      await fetchZonesForPage(zonesPage);
      toast("已从数据库删除");
    },
    [appToken, fetchZonesForPage, refreshAccounts, toast, zonesPage],
  );

  const searchRecords = useCallback(async () => {
    await fetchRecordsPage(1);
  }, [fetchRecordsPage]);

  const value = useMemo<DomainflareContextValue>(
    () => ({
      appToken,
      unlocked,
      sessionBootstrapDone,
      unlockWithToken,
      toast,
      toastState,
      accounts,
      activeAccountId,
      selectAccount,
      refreshAccounts,
      addAccount,
      deleteAccount,
      zones,
      zonesFilter,
      setZonesFilter,
      zonesPage,
      zonesTotalPages,
      zonesPageInfo,
      goZonesPrev: () => setZonesPage((p) => Math.max(1, p - 1)),
      goZonesNext: () => setZonesPage((p) => Math.min(zonesTotalPages, p + 1)),
      refreshZonesList: () => void fetchZonesForPage(1),
      activeZoneId,
      selectZone,
      filteredZones,
      recordsPage,
      recordsTotalPages,
      recordsPageInfo,
      goRecordsPrev: () => setRecordsPage((p) => Math.max(1, p - 1)),
      goRecordsNext: () =>
        setRecordsPage((p) => Math.min(recordsTotalPages, p + 1)),
      fltType,
      setFltType,
      fltName,
      setFltName,
      fltContent,
      setFltContent,
      recordsStatus,
      records,
      searchRecords,
      cf,
      activeZone,
    }),
    [
      accounts,
      activeAccountId,
      activeZone,
      activeZoneId,
      addAccount,
      appToken,
      cf,
      deleteAccount,
      filteredZones,
      fltContent,
      fltName,
      fltType,
      records,
      recordsPage,
      recordsPageInfo,
      recordsStatus,
      recordsTotalPages,
      refreshAccounts,
      searchRecords,
      selectAccount,
      selectZone,
      sessionBootstrapDone,
      toast,
      toastState,
      unlockWithToken,
      unlocked,
      zones,
      zonesFilter,
      zonesPage,
      zonesPageInfo,
      zonesTotalPages,
    ],
  );

  return (
    <DomainflareContext.Provider value={value}>{children}</DomainflareContext.Provider>
  );
}
