import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import path from "node:path";
import { createRepoSchema } from "@pijulab/shared";
import { createRepo, findRepoByOwnerUsername, findUserByUsername } from "../db/queries.js";
import { sessionMiddleware, requireAuth } from "../middleware/auth.js";
import { PijulRepo } from "../pijul/index.js";
import { env } from "../env.js";

function repoPath(ownerUsername: string, repoName: string): string {
  return path.join(env.PIJULAB_REPOS_DIR, ownerUsername, repoName);
}

export const reposRouter = new Hono()
  .use(sessionMiddleware)

  .post("/", requireAuth, zValidator("json", createRepoSchema), async (c) => {
    const user = c.get("user")!;
    const { name, description, visibility } = c.req.valid("json");

    const existing = await findRepoByOwnerUsername(user.username, name);
    if (existing) return c.json({ message: "Repository already exists" }, 409);

    const absPath = repoPath(user.username, name);
    await PijulRepo.init(absPath);

    const repoId = await createRepo({
      ownerId: user.id,
      name,
      ...(description !== undefined ? { description } : {}),
      visibility,
    });

    return c.json({ id: repoId, name, description: description ?? null, visibility }, 201);
  })

  .get("/:owner/:name", async (c) => {
    const { owner, name } = c.req.param();
    const repo = await findRepoByOwnerUsername(owner, name);
    if (!repo) return c.json({ message: "Not found" }, 404);

    const currentUser = c.get("user");
    if (repo.visibility === "private" && currentUser?.id !== repo.owner_id) {
      return c.json({ message: "Not found" }, 404);
    }

    return c.json({
      id: repo.id,
      name: repo.name,
      description: repo.description,
      visibility: repo.visibility,
      defaultChannel: repo.default_channel,
      owner: { id: repo.owner_id, username: repo.owner_username, displayName: repo.owner_display_name },
      createdAt: repo.created_at,
      updatedAt: repo.updated_at,
    });
  })

  .get("/:owner/:name/channels", async (c) => {
    const { owner, name } = c.req.param();
    const repo = await findRepoByOwnerUsername(owner, name);
    if (!repo) return c.json({ message: "Not found" }, 404);

    const pijul = PijulRepo.open(repoPath(owner, name));
    const channels = await pijul.listChannels();
    return c.json(channels);
  })

  .get("/:owner/:name/tree", async (c) => {
    const { owner, name } = c.req.param();
    const channel = c.req.query("channel") ?? "main";
    const subpath = c.req.query("path") ?? "";

    const repo = await findRepoByOwnerUsername(owner, name);
    if (!repo) return c.json({ message: "Not found" }, 404);

    const pijul = PijulRepo.open(repoPath(owner, name));
    const tree = await pijul.listTree(channel, subpath);
    return c.json(tree);
  })

  .get("/:owner/:name/file", async (c) => {
    const { owner, name } = c.req.param();
    const channel = c.req.query("channel") ?? "main";
    const filePath = c.req.query("path") ?? "";
    if (!filePath) return c.json({ message: "path is required" }, 400);

    const repo = await findRepoByOwnerUsername(owner, name);
    if (!repo) return c.json({ message: "Not found" }, 404);

    const pijul = PijulRepo.open(repoPath(owner, name));
    try {
      const content = await pijul.readFile(channel, filePath);
      return new Response(content, {
        headers: { "Content-Type": "text/plain; charset=utf-8" },
      });
    } catch {
      return c.json({ message: "File not found" }, 404);
    }
  })

  .get("/:owner/:name/log", async (c) => {
    const { owner, name } = c.req.param();
    const channel = c.req.query("channel") ?? "main";
    const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);

    const repo = await findRepoByOwnerUsername(owner, name);
    if (!repo) return c.json({ message: "Not found" }, 404);

    const pijul = PijulRepo.open(repoPath(owner, name));
    const changes = await pijul.log(channel, limit);
    return c.json(changes);
  })

  .get("/:owner/:name/change/:hash", async (c) => {
    const { owner, name, hash } = c.req.param();

    const repo = await findRepoByOwnerUsername(owner, name);
    if (!repo) return c.json({ message: "Not found" }, 404);

    const pijul = PijulRepo.open(repoPath(owner, name));
    const change = await pijul.getChange(hash);
    if (!change) return c.json({ message: "Change not found" }, 404);
    return c.json(change);
  });