import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  AlertCircle,
  Building,
  ChevronDown,
  ChevronRight,
  RefreshCw,
  RotateCcw,
  Save,
  UserPlus,
  Users,
  X,
} from "lucide-react";
import { HttpError } from "../../shared/api/errors";
import {
  AllowedAction,
  DocumentType,
  EnabledStatus,
  SubjectType,
  VisibilityType,
  type VisibilityType as VisibilityTypeCode,
} from "../../shared/api/enums";
import { notifyContentChanged } from "../../shared/state/contentRefresh";
import {
  flattenOrganizations,
  useOrganizationTree,
  useUserDirectory,
} from "../directory/directoryState";
import type { OrganizationNode } from "../directory/directoryTypes";
import { useDictionaries } from "../dictionaries/dictionaryContext";
import { invalidateDetail } from "../documents/documentState";
import type { DocumentSummary } from "../documents/documentTypes";
import { permissionApi } from "./permissionApi";
import {
  buildSavePermissionsRequest,
  clonePermissionDraft,
  createPermissionEntry,
  transitionVisibility,
  type PermissionDraft,
} from "./permissionState";
import type { PermissionDetail, PermissionEntry } from "./permissionTypes";

const NAVY = "#1A5299";
const BLUE = "#3191eb";
const WHITE = "#FFFFFF";
const PALE = "#EBF4FD";

const ACTIONS: Array<{
  key: "canView" | "canDownload" | "canEdit" | "canManagePermission" | "canDelete";
  code: string;
}> = [
  { key: "canView", code: "VIEW" },
  { key: "canDownload", code: "DOWNLOAD" },
  { key: "canEdit", code: "EDIT" },
  { key: "canManagePermission", code: "CONFIG_PERMISSION" },
  { key: "canDelete", code: "DELETE" },
];

interface PermissionModalProps {
  document: DocumentSummary;
  onClose: () => void;
  onChanged?: () => void;
}

function toHttpError(error: unknown): HttpError {
  return error instanceof HttpError
    ? error
    : new HttpError({
        httpStatus: 0,
        code: "INTERNAL_ERROR",
        message: "权限请求失败",
        details: null,
        requestId: "unknown",
        cause: error,
      });
}

function errorDescription(error: HttpError): string {
  if (error.httpStatus === 403 || error.code === "FORBIDDEN") return "当前账号无权限配置能力";
  if (error.code === "DUPLICATE_PERMISSION_SUBJECT") return "同一组织或人员不能重复配置";
  if (error.code === "SUB_PLAN_PERMISSION_INHERITED") return "子方案权限继承主案，不能独立保存";
  if (error.code === "ATTACHMENT_HAS_NO_ACL") return "共享附件无需单独配置权限";
  if (error.code === "DATA_VERSION_CONFLICT") return "权限已被其他用户修改，请核对后再次保存";
  if (error.code === "RESOURCE_NOT_FOUND" || error.httpStatus === 404) return "文档不存在或已被删除";
  if (error.httpStatus === 0) return "网络请求失败；现有登录状态不会被清除";
  return error.message;
}

function OrganizationTreePicker({
  nodes,
  onAdd,
  existing,
}: {
  nodes: OrganizationNode[];
  onAdd: (node: OrganizationNode) => void;
  existing: ReadonlySet<string>;
}) {
  const [expanded, setExpanded] = useState<Set<string>>(
    () => new Set(nodes.map((node) => node.id)),
  );
  const renderNodes = (items: OrganizationNode[], depth: number) =>
    items.map((node) => {
      const open = expanded.has(node.id);
      return (
        <div key={node.id}>
          <div className="flex items-center gap-2 py-1.5" style={{ paddingLeft: depth * 14 }}>
            {node.children.length ? (
              <button
                type="button"
                onClick={() => setExpanded((current) => {
                  const next = new Set(current);
                  if (next.has(node.id)) next.delete(node.id);
                  else next.add(node.id);
                  return next;
                })}
              >
                {open ? <ChevronDown size={13} /> : <ChevronRight size={13} />}
              </button>
            ) : <span className="w-[13px]" />}
            <span className="text-xs font-bold flex-1">{node.orgName}（{node.orgCode}）</span>
            <button
              type="button"
              disabled={existing.has(node.id)}
              onClick={() => onAdd(node)}
              className="text-xs font-black disabled:opacity-40"
              style={{ color: BLUE }}
            >
              {existing.has(node.id) ? "已添加" : "添加"}
            </button>
          </div>
          {open && node.children.length > 0 && renderNodes(node.children, depth + 1)}
        </div>
      );
    });
  return <>{renderNodes(nodes, 0)}</>;
}

export function PermissionModal({
  document,
  onClose,
  onChanged,
}: PermissionModalProps) {
  const dictionaries = useDictionaries();
  const isAttachment = document.documentType === DocumentType.ATTACHMENT;
  const isSubPlan = document.documentType === DocumentType.SUB_PLAN;
  const editable =
    document.documentType === DocumentType.MAIN &&
    document.allowedActions.includes(AllowedAction.CONFIG_PERMISSION);
  const [detail, setDetail] = useState<PermissionDetail | null>(null);
  const [baseline, setBaseline] = useState<PermissionDraft | null>(null);
  const [draft, setDraft] = useState<PermissionDraft | null>(null);
  const [loading, setLoading] = useState(!isAttachment);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<HttpError | null>(null);
  const [notice, setNotice] = useState("");
  const [retryVersion, setRetryVersion] = useState(0);
  const requestVersion = useRef(0);
  const requestController = useRef<AbortController | null>(null);

  const [showOrganizations, setShowOrganizations] = useState(false);
  const [organizationKeyword, setOrganizationKeyword] = useState("");
  const [organizationRetry, setOrganizationRetry] = useState(0);
  const organizationState = useOrganizationTree(
    organizationKeyword,
    organizationRetry,
  );
  const [showUsers, setShowUsers] = useState(false);
  const [userKeyword, setUserKeyword] = useState("");
  const [userOrganizationId, setUserOrganizationId] = useState("");
  const [includeDescendants, setIncludeDescendants] = useState(true);
  const [userPage, setUserPage] = useState(1);
  const [userRetry, setUserRetry] = useState(0);
  const userState = useUserDirectory({
    organizationId: userOrganizationId || undefined,
    includeDescendants,
    keyword: userKeyword || undefined,
    status: EnabledStatus.ENABLED,
    page: userPage,
    pageSize: 10,
  }, userRetry);

  const loadPermissions = useCallback(async (preserveDraft = false) => {
    if (isAttachment) return;
    const version = ++requestVersion.current;
    requestController.current?.abort();
    const controller = new AbortController();
    requestController.current = controller;
    setLoading(true);
    setError(null);
    try {
      const result = await permissionApi.get(document.id, controller.signal);
      if (version !== requestVersion.current) return;
      const nextBaseline = clonePermissionDraft(result.data);
      setDetail(result.data);
      setBaseline(nextBaseline);
      if (preserveDraft) {
        setDraft((current) => current
          ? { ...current, documentRowVersion: result.data.documentRowVersion }
          : nextBaseline);
      } else {
        setDraft(nextBaseline);
      }
    } catch (loadError) {
      if (controller.signal.aborted || version !== requestVersion.current) return;
      setDetail(null);
      setBaseline(null);
      if (!preserveDraft) setDraft(null);
      setError(toHttpError(loadError));
    } finally {
      if (version === requestVersion.current) setLoading(false);
    }
  }, [document.id, isAttachment]);

  useEffect(() => {
    void loadPermissions();
    return () => {
      requestVersion.current += 1;
      requestController.current?.abort();
    };
  }, [loadPermissions, retryVersion]);

  const existingSubjects = useMemo(
    () => new Set((draft?.entries ?? []).map((entry) => `${entry.subjectType}:${entry.subjectId}`)),
    [draft?.entries],
  );

  const organizationOptions = useMemo(
    () => flattenOrganizations(organizationState.tree),
    [organizationState.tree],
  );

  const changeVisibility = (visibilityType: VisibilityTypeCode) => {
    if (!draft || draft.visibilityType === visibilityType) return;
    const removedCount = visibilityType === VisibilityType.ALL_AUTHENTICATED
      ? draft.entries.length
      : visibilityType === VisibilityType.ORGANIZATION
        ? draft.entries.filter((entry) => entry.subjectType === SubjectType.USER).length
        : 0;
    if (
      removedCount > 0 &&
      !window.confirm(
        visibilityType === VisibilityType.ALL_AUTHENTICATED
          ? `切换后将清除当前 ${removedCount} 条权限项，是否继续？`
          : `切换后将移除当前 ${removedCount} 条人员权限，只保留组织权限，是否继续？`,
      )
    ) {
      return;
    }
    setDraft({
      ...draft,
      visibilityType,
      entries: transitionVisibility(draft.entries, visibilityType),
    });
    if (visibilityType !== VisibilityType.CUSTOM) setShowUsers(false);
    setNotice("");
  };

  const addSubject = (
    subjectType: typeof SubjectType.ORG | typeof SubjectType.USER,
    subjectId: string,
    subjectName: string,
  ) => {
    if (!draft) return;
    const key = `${subjectType}:${subjectId}`;
    if (existingSubjects.has(key)) {
      setNotice("该主体已存在，不能重复添加");
      return;
    }
    setDraft({
      ...draft,
      entries: [
        ...draft.entries,
        createPermissionEntry(subjectType, subjectId, subjectName),
      ],
    });
    setNotice("");
  };

  const updateEntry = (
    index: number,
    patch: Partial<PermissionEntry>,
  ) => {
    if (!draft) return;
    setDraft({
      ...draft,
      entries: draft.entries.map((entry, entryIndex) =>
        entryIndex === index ? { ...entry, ...patch } : entry),
    });
  };

  const savePermissions = async () => {
    if (!draft || saving || !editable || isSubPlan || isAttachment) return;
    setSaving(true);
    setError(null);
    setNotice("");
    try {
      const request = buildSavePermissionsRequest(draft);
      const result = await permissionApi.save(document.id, request);
      const savedDraft = clonePermissionDraft(result.data);
      setDetail(result.data);
      setBaseline(savedDraft);
      setDraft(savedDraft);
      setNotice("权限全集已保存并重新加载");
      invalidateDetail("document", document.id);
      notifyContentChanged("documents");
      onChanged?.();
    } catch (saveError) {
      const httpError = saveError instanceof Error &&
        !(saveError instanceof HttpError)
        ? new HttpError({
            httpStatus: 400,
            code: "INVALID_ARGUMENT",
            message: saveError.message,
            details: null,
            requestId: "local-validation",
            cause: saveError,
          })
        : toHttpError(saveError);
      setError(httpError);
      if (httpError.code === "DATA_VERSION_CONFLICT") {
        setNotice("权限已发生变化，请核对后重新保存。");
        await loadPermissions(true);
        setError(httpError);
        invalidateDetail("document", document.id);
        notifyContentChanged("documents");
        onChanged?.();
      }
    } finally {
      setSaving(false);
    }
  };

  const orgCount = draft?.entries.filter((entry) => entry.subjectType === SubjectType.ORG).length ?? 0;
  const userCount = draft?.entries.filter((entry) => entry.subjectType === SubjectType.USER).length ?? 0;

  return (
    <div className="fixed inset-0 z-[80] flex items-center justify-center p-5" style={{ background: "rgba(26,82,153,.55)" }} onClick={onClose}>
      <div className="w-[1080px] max-w-full h-[86vh] flex flex-col shadow-2xl" style={{ background: WHITE, color: NAVY }} onClick={(event) => event.stopPropagation()}>
        <div className="h-12 px-5 flex items-center justify-between text-white" style={{ background: NAVY }}>
          <div>
            <span className="font-black">{isSubPlan ? "继承权限" : isAttachment ? "共享附件权限" : "主案权限配置"}</span>
            <span className="ml-3 text-xs opacity-75">{document.documentName}</span>
          </div>
          <button onClick={onClose} disabled={saving}><X size={18} /></button>
        </div>

        {isAttachment ? (
          <div className="flex-1 flex items-center justify-center p-10">
            <div className="max-w-xl p-7 text-center font-bold leading-8" style={{ background: PALE }}>
              共享附件对所有已登录用户可见，无需单独配置权限。
            </div>
          </div>
        ) : loading && !draft ? (
          <div className="flex-1 flex items-center justify-center font-bold"><RefreshCw size={17} className="animate-spin mr-2" />正在加载权限…</div>
        ) : error && !draft ? (
          <div className="flex-1 flex flex-col items-center justify-center font-bold">
            <AlertCircle size={22} className="mb-3" />
            <span>{errorDescription(error)}</span>
            {error.requestId && <span className="text-xs mt-2">错误编号：{error.requestId}</span>}
            <button className="mt-4 px-4 py-2 text-white text-sm font-black" style={{ background: BLUE }} onClick={() => setRetryVersion((value) => value + 1)}>重试</button>
          </div>
        ) : draft && detail ? (
          <>
            <div className="px-5 py-3 border-b flex items-center gap-5 text-sm font-bold" style={{ borderColor: "rgba(49,145,235,.2)", background: PALE }}>
              <span>组织 {orgCount} 项</span>
              <span>人员 {userCount} 项</span>
              <span className="ml-auto">{detail.inherited ? "动态继承" : "主案自有权限"}</span>
            </div>
            {isSubPlan && (
              <div className="mx-5 mt-4 p-3 text-sm font-black" style={{ background: PALE }}>
                子方案权限继承所属主案，不能单独配置。
              </div>
            )}

            <div className="px-5 py-4 flex items-center gap-3">
              <span className="text-sm font-black">可见范围</span>
              {dictionaries.options("visibilityTypes").map((option) => {
                const value = option.code as VisibilityTypeCode;
                return (
                <label key={option.code} className="flex items-center gap-1.5 text-sm font-bold">
                  <input
                    type="radio"
                    checked={draft.visibilityType === value}
                    disabled={!editable || detail.inherited || saving}
                    onChange={() => changeVisibility(value)}
                  />
                  {option.label}
                </label>
                );
              })}
              <span className="ml-auto text-xs font-bold opacity-65">
                {draft.visibilityType === VisibilityType.ALL_AUTHENTICATED
                  ? "通过状态和密级前置检查的全部登录用户可访问。"
                  : draft.visibilityType === VisibilityType.ORGANIZATION
                    ? "组织权限覆盖该组织及其有效下级组织，由后端执行。"
                    : "允许组织与人员混合，也允许空集合。"}
              </span>
            </div>

            {editable && !detail.inherited && draft.visibilityType !== VisibilityType.ALL_AUTHENTICATED && (
              <div className="px-5 pb-3 flex gap-2">
                <button onClick={() => setShowOrganizations((value) => !value)} className="px-3 py-2 text-xs font-black border flex items-center gap-1" style={{ borderColor: BLUE }}>
                  <Building size={14} />选择组织
                </button>
                {draft.visibilityType === VisibilityType.CUSTOM && (
                  <button onClick={() => setShowUsers((value) => !value)} className="px-3 py-2 text-xs font-black border flex items-center gap-1" style={{ borderColor: BLUE }}>
                    <UserPlus size={14} />选择人员
                  </button>
                )}
              </div>
            )}

            {(showOrganizations || showUsers) && editable && !detail.inherited && (
              <div className="mx-5 mb-3 grid grid-cols-2 gap-3 max-h-[190px]">
                {showOrganizations && (
                  <div className="border p-3 overflow-auto">
                    <div className="flex gap-2 mb-2">
                      <input value={organizationKeyword} onChange={(event) => setOrganizationKeyword(event.target.value)} placeholder="组织名称或编码" className="flex-1 border px-2 h-8 text-xs font-bold outline-none" />
                      <button onClick={() => setOrganizationRetry((value) => value + 1)}><RefreshCw size={14} /></button>
                    </div>
                    {organizationState.status === "loading" && <div className="text-xs font-bold">正在加载组织树…</div>}
                    {organizationState.status === "error" && <div className="text-xs font-bold">{organizationState.error?.message} {organizationState.error?.requestId}</div>}
                    {organizationState.status === "empty" && <div className="text-xs font-bold opacity-60">无匹配组织</div>}
                    <OrganizationTreePicker
                      nodes={organizationState.tree}
                      existing={new Set(draft.entries.filter((entry) => entry.subjectType === SubjectType.ORG).map((entry) => entry.subjectId))}
                      onAdd={(node) => addSubject(SubjectType.ORG, node.id, node.orgName)}
                    />
                  </div>
                )}
                {showUsers && draft.visibilityType === VisibilityType.CUSTOM && (
                  <div className="border p-3 overflow-auto">
                    <div className="grid grid-cols-2 gap-2 mb-2">
                      <input value={userKeyword} onChange={(event) => { setUserKeyword(event.target.value); setUserPage(1); }} placeholder="姓名或用户名" className="border px-2 h-8 text-xs font-bold outline-none" />
                      <select value={userOrganizationId} onChange={(event) => { setUserOrganizationId(event.target.value); setUserPage(1); }} className="border px-2 text-xs font-bold">
                        <option value="">全部组织</option>
                        {organizationOptions.map((org) => <option key={org.id} value={org.id}>{org.orgName}</option>)}
                      </select>
                    </div>
                    <label className="text-xs font-bold flex items-center gap-1 mb-2"><input type="checkbox" checked={includeDescendants} onChange={(event) => setIncludeDescendants(event.target.checked)} />包含下级组织人员</label>
                    {userState.status === "loading" && <div className="text-xs font-bold">正在加载人员…</div>}
                    {userState.status === "error" && <div className="text-xs font-bold">{userState.error?.message} {userState.error?.requestId}<button onClick={() => setUserRetry((value) => value + 1)}>重试</button></div>}
                    {userState.data.items.map((user) => (
                      <div key={user.id} className="flex items-center gap-2 py-1.5 border-b">
                        <Users size={13} />
                        <span className="text-xs font-bold flex-1">{user.realName}（{user.username}）· {user.organizationName}</span>
                        <button
                          disabled={existingSubjects.has(`${SubjectType.USER}:${user.id}`)}
                          onClick={() => addSubject(SubjectType.USER, user.id, user.realName)}
                          className="text-xs font-black disabled:opacity-40"
                          style={{ color: BLUE }}
                        >
                          {existingSubjects.has(`${SubjectType.USER}:${user.id}`) ? "已添加" : "添加"}
                        </button>
                      </div>
                    ))}
                    {userState.data.totalPages > 1 && <div className="flex justify-end gap-2 mt-2 text-xs font-black"><button disabled={userPage <= 1} onClick={() => setUserPage((value) => value - 1)}>上页</button><span>{userPage}/{userState.data.totalPages}</span><button disabled={userPage >= userState.data.totalPages} onClick={() => setUserPage((value) => value + 1)}>下页</button></div>}
                  </div>
                )}
              </div>
            )}

            <div className="flex-1 overflow-auto px-5">
              {draft.entries.length === 0 ? (
                <div className="p-10 text-center text-sm font-bold opacity-55">
                  {draft.visibilityType === VisibilityType.ALL_AUTHENTICATED
                    ? "当前无需添加权限对象"
                    : "当前权限集合为空"}
                </div>
              ) : (
                <table className="w-full border-collapse">
                  <thead className="sticky top-0">
                    <tr style={{ background: NAVY, color: WHITE }}>
                      <th className="px-3 py-2 text-left text-xs font-black">主体</th>
                      {ACTIONS.map((action) => <th key={action.key} className="px-3 py-2 text-center text-xs font-black">{dictionaries.label("allowedActions", action.code)}</th>)}
                      <th className="px-3 py-2 text-center text-xs font-black">操作</th>
                    </tr>
                  </thead>
                  <tbody>
                    {draft.entries.map((entry, index) => (
                      <tr key={`${entry.subjectType}:${entry.subjectId}`} className="border-b" style={{ borderColor: "rgba(49,145,235,.12)" }}>
                        <td className="px-3 py-2"><div className="text-sm font-black">{entry.subjectName}</div><div className="text-[11px] opacity-60">{dictionaries.label("subjectTypes", entry.subjectType)}</div></td>
                        {ACTIONS.map((action) => (
                          <td key={action.key} className="px-3 py-2 text-center">
                            <input
                              type="checkbox"
                              checked={entry[action.key]}
                              disabled={!editable || detail.inherited || saving}
                              onChange={(event) => updateEntry(index, { [action.key]: event.target.checked })}
                            />
                          </td>
                        ))}
                        <td className="px-3 py-2 text-center">
                          {editable && !detail.inherited
                            ? <button onClick={() => setDraft({ ...draft, entries: draft.entries.filter((_, itemIndex) => itemIndex !== index) })} className="text-xs font-black" style={{ color: BLUE }}>移除</button>
                            : <span className="text-xs font-bold opacity-55">只读</span>}
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              )}
            </div>

            <div className="px-5 py-3 border-t" style={{ borderColor: "rgba(49,145,235,.2)" }}>
              {error && <div className="mb-2 text-sm font-black"><AlertCircle size={14} className="inline mr-1" />{errorDescription(error)}{error.requestId ? `（错误编号：${error.requestId}）` : ""}</div>}
              {notice && <div className="mb-2 text-sm font-black">{notice}</div>}
              <div className="flex justify-end gap-2">
                {editable && !detail.inherited && (
                  <>
                    <button
                      onClick={() => { if (baseline) setDraft({ ...baseline, entries: baseline.entries.map((entry) => ({ ...entry })) }); setError(null); setNotice("已重置尚未保存的修改。"); }}
                      disabled={saving}
                      className="px-4 py-2 text-sm font-black border flex items-center gap-1"
                      style={{ borderColor: BLUE }}
                    >
                      <RotateCcw size={14} />重置本地表单
                    </button>
                    <button onClick={() => void savePermissions()} disabled={saving || dictionaries.status !== "success"} className="px-4 py-2 text-sm font-black text-white flex items-center gap-1 disabled:opacity-50" style={{ background: BLUE }}>
                      {saving ? <RefreshCw size={14} className="animate-spin" /> : <Save size={14} />}
                      {saving ? "正在保存…" : "保存权限全集"}
                    </button>
                  </>
                )}
                <button onClick={onClose} disabled={saving} className="px-4 py-2 text-sm font-black border" style={{ borderColor: BLUE }}>关闭</button>
              </div>
            </div>
          </>
        ) : null}
      </div>
    </div>
  );
}
