import { execSync } from "node:child_process";
import type { Hono } from "hono";
import { z } from "zod";
import * as gitUtils from "../git-utils.js";
import type { PRPoller } from "../pr-poller.js";
import { parseBody } from "../validation.js";

// ─── Zod Schemas ──────────────────────────────────────────────────

const gitFetchSchema = z.object({
  repoRoot: z.string().min(1, "repoRoot is required"),
});

const gitWorktreeCreateSchema = z.object({
  repoRoot: z.string().min(1, "repoRoot is required"),
  branch: z.string().min(1, "branch is required").max(500),
  baseBranch: z.string().max(500).optional(),
  createBranch: z.boolean().optional(),
});

const gitWorktreeDeleteSchema = z.object({
  repoRoot: z.string().min(1, "repoRoot is required"),
  worktreePath: z.string().min(1, "worktreePath is required"),
  force: z.boolean().optional(),
});

const gitPullSchema = z.object({
  cwd: z.string().min(1, "cwd is required"),
});

export function registerGitRoutes(api: Hono, prPoller?: PRPoller): void {
  api.get("/git/repo-info", async (c) => {
    const path = c.req.query("path");
    if (!path) return c.json({ error: "path required" }, 400);
    const info = await gitUtils.getRepoInfo(path);
    if (!info) return c.json({ error: "Not a git repository" }, 400);
    return c.json(info);
  });

  api.get("/git/branches", async (c) => {
    const repoRoot = c.req.query("repoRoot");
    if (!repoRoot) return c.json({ error: "repoRoot required" }, 400);
    try {
      return c.json(await gitUtils.listBranches(repoRoot));
    } catch (e: unknown) {
      return c.json({ error: e instanceof Error ? e.message : String(e) }, 500);
    }
  });

  api.post("/git/fetch", async (c) => {
    const parsed = await parseBody(c, gitFetchSchema);
    if (!parsed.success) return parsed.response;
    return c.json(await gitUtils.gitFetch(parsed.data.repoRoot));
  });

  api.get("/git/worktrees", async (c) => {
    const repoRoot = c.req.query("repoRoot");
    if (!repoRoot) return c.json({ error: "repoRoot required" }, 400);
    return c.json(await gitUtils.listWorktrees(repoRoot));
  });

  api.post("/git/worktree", async (c) => {
    const parsed = await parseBody(c, gitWorktreeCreateSchema);
    if (!parsed.success) return parsed.response;
    const { repoRoot, branch, baseBranch, createBranch } = parsed.data;
    const result = await gitUtils.ensureWorktree(repoRoot, branch, { baseBranch, createBranch });
    return c.json(result);
  });

  api.delete("/git/worktree", async (c) => {
    const parsed = await parseBody(c, gitWorktreeDeleteSchema);
    if (!parsed.success) return parsed.response;
    const { repoRoot, worktreePath, force } = parsed.data;
    const result = await gitUtils.removeWorktree(repoRoot, worktreePath, { force });
    return c.json(result);
  });

  api.post("/git/pull", async (c) => {
    const parsed = await parseBody(c, gitPullSchema);
    if (!parsed.success) return parsed.response;
    const { cwd } = parsed.data;
    const result = await gitUtils.gitPull(cwd);
    let git_ahead = 0;
    let git_behind = 0;
    try {
      const counts = execSync(
        "git rev-list --left-right --count @{upstream}...HEAD",
        {
          cwd,
          encoding: "utf-8",
          timeout: 3000,
        },
      ).trim();
      const [behind, ahead] = counts.split(/\s+/).map(Number);
      git_ahead = ahead || 0;
      git_behind = behind || 0;
    } catch {
      // no upstream
    }
    return c.json({ ...result, git_ahead, git_behind });
  });

  api.get("/git/pr-status", async (c) => {
    const cwd = c.req.query("cwd");
    const branch = c.req.query("branch");
    if (!cwd || !branch) return c.json({ error: "cwd and branch required" }, 400);

    if (prPoller) {
      const cached = prPoller.getCached(cwd, branch);
      if (cached) return c.json(cached);
    }

    const { initGhAvailability, fetchPRInfoAsync } = await import("../github-pr.js");
    if (!(await initGhAvailability())) {
      return c.json({ available: false, pr: null });
    }

    const pr = await fetchPRInfoAsync(cwd, branch);
    return c.json({ available: true, pr });
  });

  api.get("/git/open-prs", async (c) => {
    const cwd = c.req.query("cwd");
    if (!cwd) return c.json({ error: "cwd is required" }, 400);

    const { initGhAvailability, listOpenPRsAsync } = await import("../github-pr.js");
    if (!(await initGhAvailability())) {
      return c.json({ available: false, prs: [] });
    }

    const prs = await listOpenPRsAsync(cwd);
    return c.json({ available: true, prs });
  });
}
