import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { Worktree } from "@/worktree"
import { Instance } from "@/project/instance"
import { Project } from "@/project/project"
import { Review } from "@/saeeol/review/review"
import { WorktreeDiff } from "@/saeeol/review/worktree-diff"
import * as Log from "@saeeol/core/util/log"
import { Snapshot } from "@/snapshot"
import { lazy } from "@/util/lazy"
import { jsonRequest } from "./trace"
import { errors } from "../../error"

export const ReviewRoutes = lazy(() =>
  new Hono()
    .post(
      "/worktree",
      describeRoute({
        summary: "Create worktree",
        description: "Create a new git worktree for the current project and run any configured startup scripts.",
        operationId: "worktree.create",
        responses: {
          200: {
            description: "Worktree created",
            content: {
              "application/json": {
                schema: resolver(Worktree.Info.zod),
              },
            },
          },
          ...errors(400),
        },
      }),
      validator("json", Worktree.CreateInput.zod.optional()),
      async (c) =>
        jsonRequest("ExperimentalRoutes.worktree.create", c, function* () {
          const body = c.req.valid("json")
          const svc = yield* Worktree.Service
          return yield* svc.create(body)
        }),
    )
    .get(
      "/worktree",
      describeRoute({
        summary: "List worktrees",
        description: "List all sandbox worktrees for the current project.",
        operationId: "worktree.list",
        responses: {
          200: {
            description: "List of worktree directories",
            content: {
              "application/json": {
                schema: resolver(z.array(z.string())),
              },
            },
          },
        },
      }),
      async (c) =>
        jsonRequest("ExperimentalRoutes.worktree.list", c, function* () {
          const svc = yield* Project.Service
          return yield* svc.sandboxes(Instance.project.id)
        }),
    )
    .delete(
      "/worktree",
      describeRoute({
        summary: "Remove worktree",
        description: "Remove a git worktree and delete its branch.",
        operationId: "worktree.remove",
        responses: {
          200: {
            description: "Worktree removed",
            content: {
              "application/json": {
                schema: resolver(z.boolean()),
              },
            },
          },
          ...errors(400),
        },
      }),
      validator("json", Worktree.RemoveInput.zod),
      async (c) =>
        jsonRequest("ExperimentalRoutes.worktree.remove", c, function* () {
          const body = c.req.valid("json")
          const worktree = yield* Worktree.Service
          const project = yield* Project.Service
          yield* worktree.remove(body)
          yield* project.removeSandbox(Instance.project.id, body.directory)
          return true
        }),
    )
    .post(
      "/worktree/reset",
      describeRoute({
        summary: "Reset worktree",
        description: "Reset a worktree branch to the primary default branch.",
        operationId: "worktree.reset",
        responses: {
          200: {
            description: "Worktree reset",
            content: {
              "application/json": {
                schema: resolver(z.boolean()),
              },
            },
          },
          ...errors(400),
        },
      }),
      validator("json", Worktree.ResetInput.zod),
      async (c) =>
        jsonRequest("ExperimentalRoutes.worktree.reset", c, function* () {
          const body = c.req.valid("json")
          const svc = yield* Worktree.Service
          yield* svc.reset(body)
          return true
        }),
    )
    .get(
      "/worktree/diff",
      describeRoute({
        summary: "Get worktree diff",
        description: "Get file diffs for a worktree compared to its base branch. Includes uncommitted changes.",
        operationId: "worktree.diff",
        responses: {
          200: {
            description: "File diffs",
            content: {
              "application/json": {
                schema: resolver(z.array(Snapshot.FileDiff.zod)),
              },
            },
          },
          ...errors(400),
        },
      }),
      validator(
        "query",
        z.object({
          base: z.string().optional().meta({ description: "Base branch or ref to diff against" }),
        }),
      ),
      async (c) => {
        const log = Log.create({ service: "worktree-diff" })
        const query = c.req.valid("query")
        const base = query.base || (await Review.getBaseBranch())
        const dir = Instance.directory
        log.info("computing diff", { dir, base })
        const diffs = await WorktreeDiff.full({ dir, base, log })
        return c.json(
          diffs.map((diff) => ({
            file: diff.file,
            before: diff.before,
            after: diff.after,
            additions: diff.additions,
            deletions: diff.deletions,
            status: diff.status,
          })),
        )
      },
    )
    .get(
      "/worktree/diff/summary",
      describeRoute({
        summary: "Get worktree diff summary",
        description: "Get lightweight file diff metadata for a worktree compared to its base branch.",
        operationId: "worktree.diffSummary",
        responses: {
          200: {
            description: "Diff summary items",
            content: {
              "application/json": {
                schema: resolver(z.array(WorktreeDiff.Item.zod)),
              },
            },
          },
          ...errors(400),
        },
      }),
      validator(
        "query",
        z.object({
          base: z.string().optional().meta({ description: "Base branch or ref to diff against" }),
        }),
      ),
      async (c) => {
        const log = Log.create({ service: "worktree-diff" })
        const query = c.req.valid("query")
        const base = query.base || (await Review.getBaseBranch())
        const dir = Instance.directory
        log.info("computing diff summary", { dir, base })
        return c.json(await WorktreeDiff.summary({ dir, base, log }))
      },
    )
    .get(
      "/worktree/diff/file",
      describeRoute({
        summary: "Get worktree diff detail",
        description: "Get full diff contents for one worktree file compared to its base branch.",
        operationId: "worktree.diffFile",
        responses: {
          200: {
            description: "Diff detail item",
            content: {
              "application/json": {
                schema: resolver(WorktreeDiff.Item.zod.nullable()),
              },
            },
          },
          ...errors(400),
        },
      }),
      validator(
        "query",
        z.object({
          base: z.string().optional().meta({ description: "Base branch or ref to diff against" }),
          file: z.string().meta({ description: "Relative file path to load diff contents for" }),
        }),
      ),
      async (c) => {
        const log = Log.create({ service: "worktree-diff" })
        const query = c.req.valid("query")
        const base = query.base || (await Review.getBaseBranch())
        const dir = Instance.directory
        log.info("computing diff detail", { dir, base, file: query.file })
        return c.json((await WorktreeDiff.detail({ dir, base, file: query.file, log })) ?? null)
      },
    )
)
