import { useCallback, useEffect, useRef, useState } from "react";
import { HttpError } from "../../shared/api/errors";
import type { PageResult } from "../../shared/api/types";
import { documentApi } from "./documentApi";
import type {
  DocumentDetail,
  DocumentListQuery,
  DocumentSummary,
  SubPlanListQuery,
} from "./documentTypes";
import { useContentRevision } from "../../shared/state/contentRefresh";

export interface RemotePageState<T> {
  data: PageResult<T> | null;
  loading: boolean;
  error: HttpError | null;
  retry: () => void;
}

const initialPageState = <T,>(): RemotePageState<T> => ({
  data: null,
  loading: true,
  error: null,
  retry: () => undefined,
});

function useDebouncedPage<T>(
  load: (signal: AbortSignal) => Promise<PageResult<T>>,
  dependencies: readonly unknown[],
  debounceKey: string | undefined,
  enabled = true,
  debounceMs = 300,
): RemotePageState<T> {
  const [state, setState] = useState<RemotePageState<T>>(initialPageState);
  const requestVersion = useRef(0);
  const previousDebounceKey = useRef(debounceKey);
  const [retryVersion, setRetryVersion] = useState(0);
  const retry = useCallback(() => setRetryVersion((value) => value + 1), []);

  useEffect(() => {
    if (!enabled) {
      setState((current) => ({ ...current, loading: false, error: null, retry }));
      return;
    }
    const version = ++requestVersion.current;
    const controller = new AbortController();
    const delay = previousDebounceKey.current === debounceKey ? 0 : debounceMs;
    previousDebounceKey.current = debounceKey;
    const timer = window.setTimeout(() => {
      setState((current) => ({ ...current, loading: true, error: null, retry }));
      void load(controller.signal)
        .then((data) => {
          if (version === requestVersion.current) {
            setState({ data, loading: false, error: null, retry });
          }
        })
        .catch((error: unknown) => {
          if (
            version !== requestVersion.current ||
            controller.signal.aborted
          ) {
            return;
          }
          setState({
            data: null,
            loading: false,
            retry,
            error:
              error instanceof HttpError
                ? error
                : new HttpError({
                    httpStatus: 0,
                    code: "INTERNAL_ERROR",
                    message: "请求失败",
                    details: null,
                    requestId: "unknown",
                    cause: error,
                  }),
          });
        });
    }, delay);

    return () => {
      window.clearTimeout(timer);
      controller.abort();
    };
    // The caller passes the complete primitive dependency set. Retry and
    // enabled are appended here so errors can be retried without mutating filters.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [...dependencies, enabled, retryVersion]);

  return { ...state, retry };
}

export function useDocumentList(
  query: DocumentListQuery,
  enabled = true,
): RemotePageState<DocumentSummary> {
  const revision = useContentRevision("documents");
  // tags 是数组，依赖比较需用稳定字符串；空数组与 undefined 都映射为空键，
  // 避免父组件每次重渲染都传新数组引用触发无限刷新。
  const tagsKey = query.tags && query.tags.length > 0 ? query.tags.join("\n") : "";
  return useDebouncedPage(
    (signal) => documentApi.list(query, signal),
    [
      query.documentType,
      query.categoryId,
      query.keyword,
      tagsKey,
      query.tagsMatch,
      query.securityLevel,
      query.visibilityType,
      query.status,
      query.updatedFrom,
      query.updatedTo,
      query.sortBy,
      query.sortDirection,
      query.page,
      query.pageSize,
      revision,
    ],
    query.keyword,
    enabled,
  );
}

export interface RemoteTagsState {
  data: string[] | null;
  loading: boolean;
  error: HttpError | null;
  retry: () => void;
}

const initialTagsState = (): RemoteTagsState => ({
  data: null,
  loading: true,
  error: null,
  retry: () => undefined,
});

/**
 * 加载左侧标签筛选条所需的全部主案标签。
 *
 * - 后端已按可见性过滤，前端无需再做权限处理；
 * - 失败时静默到空数组，避免拖垮主案列表的可用性（标签条仅是辅助筛选）。
 */
export function useDocumentTags(enabled = true): RemoteTagsState {
  const [state, setState] = useState<RemoteTagsState>(initialTagsState);
  const requestVersion = useRef(0);
  const [retryVersion, setRetryVersion] = useState(0);
  const retry = useCallback(() => setRetryVersion((value) => value + 1), []);

  useEffect(() => {
    if (!enabled) {
      setState((current) => ({ ...current, loading: false, error: null, retry }));
      return;
    }
    const version = ++requestVersion.current;
    const controller = new AbortController();
    setState((current) => ({ ...current, loading: true, error: null, retry }));
    void documentApi
      .listTags(controller.signal)
      .then((tags) => {
        if (version === requestVersion.current) {
          setState({ data: tags, loading: false, error: null, retry });
        }
      })
      .catch((error: unknown) => {
        if (version !== requestVersion.current || controller.signal.aborted) {
          return;
        }
        setState({
          data: null,
          loading: false,
          retry,
          error:
            error instanceof HttpError
              ? error
              : new HttpError({
                  httpStatus: 0,
                  code: "INTERNAL_ERROR",
                  message: "请求失败",
                  details: null,
                  requestId: "unknown",
                  cause: error,
                }),
        });
      });
    return () => {
      controller.abort();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [enabled, retryVersion]);

  return { ...state, retry };
}

export function useSubPlanList(
  mainPlanId: string | null,
  query: SubPlanListQuery,
  enabled = true,
): RemotePageState<DocumentSummary> {
  const revision = useContentRevision("documents");
  return useDebouncedPage(
    (signal) =>
      mainPlanId
        ? documentApi.subPlans(mainPlanId, query, signal)
        : Promise.resolve({
            items: [],
            page: query.page ?? 1,
            pageSize: query.pageSize ?? 20,
            total: 0,
            totalPages: 0,
          }),
    [
      mainPlanId,
      query.keyword,
      query.status,
      query.updatedFrom,
      query.updatedTo,
      query.sortBy,
      query.sortDirection,
      query.page,
      query.pageSize,
      revision,
    ],
    query.keyword,
    enabled,
  );
}

type DetailLoader = (id: string, signal?: AbortSignal) => Promise<DocumentDetail>;
const detailCache = new Map<string, DocumentDetail>();
const detailRequests = new Map<string, Promise<DocumentDetail>>();

export function loadDetailOnce(
  scope: "document" | "attachment",
  id: string,
  loader: DetailLoader,
): Promise<DocumentDetail> {
  const key = `${scope}:${id}`;
  const cached = detailCache.get(key);
  if (cached) return Promise.resolve(cached);
  const pending = detailRequests.get(key);
  if (pending) return pending;

  const request = loader(id)
    .then((detail) => {
      detailCache.set(key, detail);
      return detail;
    })
    .finally(() => {
      detailRequests.delete(key);
    });
  detailRequests.set(key, request);
  return request;
}

export function clearDetailCacheForTests(): void {
  detailCache.clear();
  detailRequests.clear();
}

export function invalidateDetail(scope: "document" | "attachment", id: string): void {
  detailCache.delete(`${scope}:${id}`);
  detailRequests.delete(`${scope}:${id}`);
}

export function cacheDetail(
  scope: "document" | "attachment",
  detail: DocumentDetail,
): void {
  detailCache.set(`${scope}:${detail.id}`, detail);
}
