import { useEffect, useMemo, useRef, useState } from "react";
import { AlertCircle, Check, RefreshCw, Search, X } from "lucide-react";
import { HttpError } from "../../shared/api/errors";
import type { AttachmentType as AttachmentTypeCode } from "../../shared/api/enums";
import { notifyContentChanged } from "../../shared/state/contentRefresh";
import { attachmentApi } from "../attachments/attachmentApi";
import { useAttachmentList } from "../attachments/attachmentState";
import { documentApi } from "../documents/documentApi";
import { invalidateDetail } from "../documents/documentState";
import type { DocumentSummary } from "../documents/documentTypes";
import { useDictionaries } from "../dictionaries/dictionaryContext";
import { bindingApi } from "./bindingApi";
import {
  dedupeAttachmentIds,
  describeBindResult,
  toggleAttachmentSelection,
} from "./bindingState";

const NAVY = "#1A5299";
const BLUE = "#3191eb";
const WHITE = "#FFFFFF";
const PALE = "#EBF4FD";
interface AttachmentBindingModalProps {
  mainPlan: DocumentSummary;
  onClose: () => void;
  onChanged: () => void;
}

interface MountedState {
  ids: Set<string>;
  loading: boolean;
  error: HttpError | null;
}

function asHttpError(error: unknown, message: string): HttpError {
  return error instanceof HttpError
    ? error
    : new HttpError({
        httpStatus: 0,
        code: "INTERNAL_ERROR",
        message,
        details: null,
        requestId: "unknown",
        cause: error,
      });
}

export function AttachmentBindingModal({
  mainPlan,
  onClose,
  onChanged,
}: AttachmentBindingModalProps) {
  const dictionaries = useDictionaries();
  const [keyword, setKeyword] = useState("");
  const [attachmentType, setAttachmentType] = useState<AttachmentTypeCode | "">("");
  const [fileExtension, setFileExtension] = useState("");
  const [updatedFrom, setUpdatedFrom] = useState("");
  const [updatedTo, setUpdatedTo] = useState("");
  const [sortBy, setSortBy] = useState<"documentName" | "updatedAt">("updatedAt");
  const [sortDirection, setSortDirection] = useState<"asc" | "desc">("desc");
  const [page, setPage] = useState(1);
  const [selected, setSelected] = useState<string[]>([]);
  const [mounted, setMounted] = useState<MountedState>({
    ids: new Set(),
    loading: true,
    error: null,
  });
  const [mountedRevision, setMountedRevision] = useState(0);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<HttpError | null>(null);
  const [notice, setNotice] = useState("");
  const mountedRequestVersion = useRef(0);

  const attachments = useAttachmentList({
    keyword: keyword.trim() || undefined,
    attachmentType: attachmentType || undefined,
    fileExtension: fileExtension.trim() || undefined,
    updatedFrom: updatedFrom ? `${updatedFrom}T00:00:00.000Z` : undefined,
    updatedTo: updatedTo ? `${updatedTo}T23:59:59.999Z` : undefined,
    sortBy,
    sortDirection,
    page,
    pageSize: 10,
  });

  useEffect(() => {
    const version = ++mountedRequestVersion.current;
    const controller = new AbortController();
    setMounted((current) => ({ ...current, loading: true, error: null }));
    void (async () => {
      const ids = new Set<string>();
      let currentPage = 1;
      let totalPages = 1;
      do {
        const result = await attachmentApi.mountedByMainPlan(
          mainPlan.id,
          { page: currentPage, pageSize: 100 },
          controller.signal,
        );
        result.items.forEach((item) => ids.add(item.id));
        totalPages = result.totalPages;
        currentPage += 1;
      } while (currentPage <= totalPages);
      if (!controller.signal.aborted && version === mountedRequestVersion.current) {
        setMounted({ ids, loading: false, error: null });
      }
    })().catch((loadError: unknown) => {
      if (!controller.signal.aborted && version === mountedRequestVersion.current) {
        setMounted({
          ids: new Set(),
          loading: false,
          error: asHttpError(loadError, "已挂载附件加载失败"),
        });
      }
    });
    return () => controller.abort();
  }, [mainPlan.id, mountedRevision]);

  const selectedCount = useMemo(
    () => selected.filter((id) => !mounted.ids.has(id)).length,
    [mounted.ids, selected],
  );

  const handleSubmit = async () => {
    if (submitting) return;
    const attachmentIds = dedupeAttachmentIds(selected);
    if (attachmentIds.length === 0) {
      setNotice("请至少选择一个未挂载附件");
      return;
    }
    setSubmitting(true);
    setError(null);
    setNotice("");
    try {
      const latest = await documentApi.detail(mainPlan.id);
      const result = await bindingApi.bind(mainPlan.id, {
        attachmentIds,
        mainPlanRowVersion: latest.rowVersion,
      });
      setNotice(describeBindResult(result.data));
      invalidateDetail("document", mainPlan.id);
      notifyContentChanged("documents", "attachments");
      setMountedRevision((value) => value + 1);
      onChanged();
    } catch (submitError) {
      const httpError = asHttpError(submitError, "挂载失败");
      setError(httpError);
      if (httpError.code === "DATA_VERSION_CONFLICT") {
        setNotice("主案已被其他用户修改，已刷新最新挂载关系；请核对保留的选择后再次提交。");
        invalidateDetail("document", mainPlan.id);
        notifyContentChanged("documents", "attachments");
        setMountedRevision((value) => value + 1);
        onChanged();
      }
    } finally {
      setSubmitting(false);
    }
  };

  const loading = attachments.loading || mounted.loading;
  const loadError = attachments.error ?? mounted.error;

  return (
    <div
      className="fixed inset-0 z-[80] flex items-center justify-center p-6"
      style={{ background: "rgba(26,82,153,.55)" }}
      onClick={onClose}
    >
      <div
        className="w-[980px] max-w-full h-[82vh] flex flex-col shadow-2xl"
        style={{ background: WHITE }}
        onClick={(event) => event.stopPropagation()}
      >
        <div className="h-12 px-5 flex items-center justify-between" style={{ background: NAVY, color: WHITE }}>
          <div>
            <span className="font-black">挂载共享附件</span>
          <span className="ml-3 text-xs opacity-75">主案：{mainPlan.documentName}</span>
          </div>
          <button onClick={onClose} disabled={submitting}><X size={18} /></button>
        </div>

        <div className="p-4 grid grid-cols-4 gap-2 border-b" style={{ borderColor: "rgba(49,145,235,.2)", color: NAVY }}>
          <label className="col-span-2 flex items-center gap-2 border px-3 h-9">
            <Search size={14} />
            <input
              value={keyword}
              onChange={(event) => { setKeyword(event.target.value); setPage(1); }}
              placeholder="搜索附件名称、概述或标签"
              className="w-full bg-transparent outline-none text-sm font-bold"
            />
          </label>
          <select
            value={attachmentType}
            onChange={(event) => { setAttachmentType(event.target.value as AttachmentTypeCode | ""); setPage(1); }}
            className="border px-2 text-sm font-bold"
          >
            <option value="">全部附件类型</option>
            {dictionaries.options("attachmentTypes").map((option) => (
              <option key={option.code} value={option.code}>{option.label}</option>
            ))}
          </select>
          <input
            value={fileExtension}
            onChange={(event) => { setFileExtension(event.target.value); setPage(1); }}
            placeholder="扩展名，如 docx"
            className="border px-3 text-sm font-bold outline-none"
          />
          <label className="text-xs font-bold">更新起始
            <input type="date" value={updatedFrom} onChange={(event) => { setUpdatedFrom(event.target.value); setPage(1); }} className="block mt-1 border h-8 px-2 w-full" />
          </label>
          <label className="text-xs font-bold">更新截止
            <input type="date" value={updatedTo} onChange={(event) => { setUpdatedTo(event.target.value); setPage(1); }} className="block mt-1 border h-8 px-2 w-full" />
          </label>
          <select value={sortBy} onChange={(event) => setSortBy(event.target.value as "documentName" | "updatedAt")} className="border px-2 text-sm font-bold">
            <option value="updatedAt">按更新时间</option>
            <option value="documentName">按名称</option>
          </select>
          <select value={sortDirection} onChange={(event) => setSortDirection(event.target.value as "asc" | "desc")} className="border px-2 text-sm font-bold">
            <option value="desc">降序</option>
            <option value="asc">升序</option>
          </select>
        </div>

        <div className="flex-1 overflow-auto">
          {loading && <div className="p-10 text-center font-bold" style={{ color: NAVY }}><RefreshCw size={16} className="inline mr-2 animate-spin" />正在加载附件和挂载关系…</div>}
          {loadError && !loading && (
            <div className="p-10 text-center font-bold" style={{ color: NAVY }}>
              <AlertCircle size={16} className="inline mr-2" />
              {loadError.httpStatus === 403 ? "当前账号无权管理附件挂载" : loadError.message}
              {loadError.requestId && <div className="text-xs mt-2">错误编号：{loadError.requestId}</div>}
            </div>
          )}
          {!loading && !loadError && attachments.data?.items.length === 0 && (
            <div className="p-10 text-center text-sm font-bold opacity-55" style={{ color: NAVY }}>暂无符合条件的共享附件</div>
          )}
          {!loading && !loadError && attachments.data && attachments.data.items.length > 0 && (
            <table className="w-full border-collapse">
              <thead className="sticky top-0">
                <tr style={{ background: PALE, color: NAVY }}>
                  {["选择", "附件名称", "类型", "扩展名", "更新时间", "状态"].map((heading) => <th key={heading} className="text-left px-4 py-3 text-xs font-black">{heading}</th>)}
                </tr>
              </thead>
              <tbody>
                {attachments.data.items.map((item) => {
                  const alreadyMounted = mounted.ids.has(item.id);
                  const checked = selected.includes(item.id);
                  return (
                    <tr key={item.id} className="border-b" style={{ borderColor: "rgba(49,145,235,.12)", color: NAVY }}>
                      <td className="px-4 py-3">
                        <input
                          type="checkbox"
                          checked={checked && !alreadyMounted}
                          disabled={alreadyMounted || submitting}
                          onChange={() => setSelected((current) => toggleAttachmentSelection(current, item.id))}
                        />
                      </td>
                  <td className="px-4 py-3"><div className="text-sm font-black">{item.documentName}</div></td>
                      <td className="px-4 py-3 text-xs font-bold">{dictionaries.label("attachmentTypes", item.attachmentType)}</td>
                      <td className="px-4 py-3 text-xs font-bold">{item.fileExtension}</td>
                      <td className="px-4 py-3 text-xs font-bold">{new Date(item.updatedAt).toLocaleString("zh-CN")}</td>
                      <td className="px-4 py-3">
                        {alreadyMounted
                          ? <span className="inline-flex items-center gap-1 text-xs font-black"><Check size={13} />已挂载</span>
                          : <span className="text-xs font-bold opacity-60">可挂载</span>}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}
        </div>

        {attachments.data && attachments.data.totalPages > 1 && (
          <div className="px-4 py-2 flex justify-end gap-4 text-xs font-black" style={{ color: NAVY }}>
            <button disabled={page <= 1 || submitting} onClick={() => setPage((value) => value - 1)}>上一页</button>
            <span>{page}/{attachments.data.totalPages}</span>
            <button disabled={page >= attachments.data.totalPages || submitting} onClick={() => setPage((value) => value + 1)}>下一页</button>
          </div>
        )}

        <div className="px-5 py-3 border-t" style={{ borderColor: "rgba(49,145,235,.2)", color: NAVY }}>
          {error && <div className="mb-2 text-sm font-black"><AlertCircle size={14} className="inline mr-1" />{error.httpStatus === 403 ? "当前账号无挂载管理权限" : error.message}{error.requestId ? `（错误编号：${error.requestId}）` : ""}</div>}
          {notice && <div className="mb-2 text-sm font-black">{notice}</div>}
          <div className="flex items-center">
            <span className="text-xs font-bold">已选择 {selectedCount} 个未挂载附件；翻页和搜索不会清除选择。</span>
            <div className="ml-auto flex gap-2">
              <button onClick={onClose} disabled={submitting} className="px-4 py-2 text-sm font-black border" style={{ borderColor: BLUE }}>取消</button>
              <button onClick={() => void handleSubmit()} disabled={submitting || selected.length === 0 || !!loadError} className="px-4 py-2 text-sm font-black text-white disabled:opacity-50" style={{ background: BLUE }}>
                {submitting ? "正在挂载…" : "确认挂载"}
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
