/**
 * `useMcpToolPermissions` — view-model hook for the "Tools MCP IdeA" tab
 * (ticket #82). Owns the durable per-project MCP tool permission document
 * (distinct from the file/command permissions in `usePermissions`) and
 * exposes read/save/reset operations. No effective-policy resolution logic
 * beyond mirroring the backend's own precedence (agent override > project
 * default > canonical read-only fallback) using ONLY data the API returns —
 * the read/write classification itself always comes from `catalogue`, never
 * a local constant (ticket #82 acceptance criteria).
 */

import { useCallback, useEffect, useState } from "react";

import type { GatewayError, McpToolPolicy, ProjectMcpToolPermissions } from "@/domain";
import { useGateways } from "@/app/di";

export interface McpToolPermissionsViewModel {
  doc: ProjectMcpToolPermissions | null;
  loading: boolean;
  busy: boolean;
  error: string | null;
  refresh: () => Promise<void>;
  saveProjectDefault: (policy: McpToolPolicy) => Promise<void>;
  saveAgentOverride: (agentId: string, policy: McpToolPolicy) => Promise<void>;
  resetAgentOverride: (agentId: string) => Promise<void>;
}

function describe(e: unknown): string {
  if (e && typeof e === "object" && "message" in e) {
    return String((e as GatewayError).message);
  }
  return String(e);
}

/**
 * Effective allowlist for `agentId`: its stored override if any, else the
 * project default, else the catalogue's read-only fallback. Pure function of
 * backend-provided data (no hardcoded tool names).
 */
export function effectiveMcpAllowedTools(
  doc: ProjectMcpToolPermissions,
  agentId: string,
): string[] {
  const override = doc.agents.find((entry) => entry.agentId === agentId);
  if (override) return override.policy.allowedTools;
  if (doc.projectDefault) return doc.projectDefault.allowedTools;
  return doc.catalogue.readOnlyTools;
}

export function useMcpToolPermissions(
  projectId: string,
): McpToolPermissionsViewModel {
  const { permission } = useGateways();
  const [doc, setDoc] = useState<ProjectMcpToolPermissions | null>(null);
  const [loading, setLoading] = useState(false);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const refresh = useCallback(async () => {
    if (!permission) return;
    setLoading(true);
    setError(null);
    try {
      setDoc(await permission.getMcpToolPermissions(projectId));
    } catch (e) {
      setError(describe(e));
    } finally {
      setLoading(false);
    }
  }, [permission, projectId]);

  useEffect(() => {
    void refresh();
  }, [refresh]);

  const saveProjectDefault = useCallback(
    async (policy: McpToolPolicy) => {
      if (!permission) return;
      setBusy(true);
      setError(null);
      try {
        setDoc(await permission.updateProjectMcpToolPermissions(projectId, policy));
      } catch (e) {
        setError(describe(e));
        throw e;
      } finally {
        setBusy(false);
      }
    },
    [permission, projectId],
  );

  const saveAgentOverride = useCallback(
    async (agentId: string, policy: McpToolPolicy) => {
      if (!permission) return;
      setBusy(true);
      setError(null);
      try {
        setDoc(
          await permission.updateAgentMcpToolPermissions(projectId, agentId, policy),
        );
      } catch (e) {
        setError(describe(e));
        throw e;
      } finally {
        setBusy(false);
      }
    },
    [permission, projectId],
  );

  const resetAgentOverride = useCallback(
    async (agentId: string) => {
      if (!permission) return;
      setBusy(true);
      setError(null);
      try {
        setDoc(
          await permission.updateAgentMcpToolPermissions(projectId, agentId, null),
        );
      } catch (e) {
        setError(describe(e));
        throw e;
      } finally {
        setBusy(false);
      }
    },
    [permission, projectId],
  );

  return {
    doc,
    loading,
    busy,
    error,
    refresh,
    saveProjectDefault,
    saveAgentOverride,
    resetAgentOverride,
  };
}
