/**
 * `useGit` — view-model hook for the Git feature (L8).
 *
 * Owns the git feature state for a given project and exposes the actions the
 * UI triggers. Consumes {@link GitGateway} exclusively through `useGateways().git`;
 * never touches `invoke()` or `@tauri-apps/api`.
 */

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

import type { GitBranches, GitCommit, GitFileStatus, GatewayError } from "@/domain";
import { useGateways } from "@/app/di";

/** What the Git UI needs from this hook. */
export interface GitViewModel {
  /** Files in the working tree (staged + unstaged). */
  files: GitFileStatus[];
  /** Branch info: list + current. */
  branches: GitBranches;
  /** Recent commit log. */
  log: GitCommit[];
  /** Whether a request is in flight. */
  busy: boolean;
  /** Last error message, or `null`. */
  error: string | null;
  /** Refresh all git state. */
  refresh: () => Promise<void>;
  /** Stage a file. */
  stage: (path: string) => Promise<void>;
  /** Unstage a file. */
  unstage: (path: string) => Promise<void>;
  /** Commit staged files with the given message. */
  commit: (message: string) => Promise<void>;
  /** Checkout (or create) a branch. */
  checkout: (branch: string) => Promise<void>;
}

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

const EMPTY_BRANCHES: GitBranches = { branches: [], current: null };
const LOG_LIMIT = 20;

export function useGit(projectId: string): GitViewModel {
  const { git } = useGateways();

  const [files, setFiles] = useState<GitFileStatus[]>([]);
  const [branches, setBranches] = useState<GitBranches>(EMPTY_BRANCHES);
  const [log, setLog] = useState<GitCommit[]>([]);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const refresh = useCallback(async () => {
    setBusy(true);
    setError(null);
    try {
      const [fileList, branchInfo, commitLog] = await Promise.all([
        git.status(projectId),
        git.branches(projectId),
        git.log(projectId, LOG_LIMIT),
      ]);
      setFiles(fileList);
      setBranches(branchInfo);
      setLog(commitLog);
    } catch (e) {
      setError(describeError(e));
    } finally {
      setBusy(false);
    }
  }, [git, projectId]);

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

  const stage = useCallback(
    async (path: string) => {
      setBusy(true);
      setError(null);
      try {
        await git.stage(projectId, path);
        setFiles((prev) =>
          prev.map((f) => (f.path === path ? { ...f, staged: true } : f)),
        );
      } catch (e) {
        setError(describeError(e));
      } finally {
        setBusy(false);
      }
    },
    [git, projectId],
  );

  const unstage = useCallback(
    async (path: string) => {
      setBusy(true);
      setError(null);
      try {
        await git.unstage(projectId, path);
        setFiles((prev) =>
          prev.map((f) => (f.path === path ? { ...f, staged: false } : f)),
        );
      } catch (e) {
        setError(describeError(e));
      } finally {
        setBusy(false);
      }
    },
    [git, projectId],
  );

  const commit = useCallback(
    async (message: string) => {
      setBusy(true);
      setError(null);
      try {
        const newCommit = await git.commit(projectId, message);
        // Remove committed (staged) files from the list.
        setFiles((prev) => prev.filter((f) => !f.staged));
        setLog((prev) => [newCommit, ...prev].slice(0, LOG_LIMIT));
      } catch (e) {
        setError(describeError(e));
      } finally {
        setBusy(false);
      }
    },
    [git, projectId],
  );

  const checkout = useCallback(
    async (branch: string) => {
      setBusy(true);
      setError(null);
      try {
        await git.checkout(projectId, branch);
        setBranches((prev) => ({
          branches: prev.branches.includes(branch)
            ? prev.branches
            : [...prev.branches, branch],
          current: branch,
        }));
      } catch (e) {
        setError(describeError(e));
      } finally {
        setBusy(false);
      }
    },
    [git, projectId],
  );

  return {
    files,
    branches,
    log,
    busy,
    error,
    refresh,
    stage,
    unstage,
    commit,
    checkout,
  };
}
