import type { PermissionRole, PrismaClient } from "@prisma/client";
import type { FastifyInstance } from "fastify";
import {
  grantTeamProjectAccess,
  listProjectTeamAccess,
  revokeTeamProjectAccess,
} from "../../permissions/projectTeamAccess.js";
import { requireProjectPermission, type GuardDeps } from "../auth/guards.js";
import { handleRouteError } from "../errors.js";

const ROLES: readonly PermissionRole[] = ["READ", "EDIT", "MANAGE"];

export async function registerAccessRoutes(
  app: FastifyInstance,
  config: { readonly prisma: PrismaClient; readonly sessionSecret: string },
): Promise<void> {
  const guardDeps: GuardDeps = { prisma: config.prisma, sessionSecret: config.sessionSecret };

  app.get("/api/org/:orgSlug/projects/:projectId/team-access", async (request, reply) => {
    try {
      const { orgSlug, projectId } = request.params as { orgSlug: string; projectId: string };
      const auth = await requireProjectPermission(request, reply, guardDeps, {
        orgSlug,
        projectId,
        action: "project.read",
        allowOrgAdminOversight: true,
      });
      if (auth === null) return;
      return { access: await listProjectTeamAccess(config.prisma, projectId) };
    } catch (err) {
      return handleRouteError(reply, err);
    }
  });

  app.put("/api/org/:orgSlug/projects/:projectId/team-access", async (request, reply) => {
    try {
      const { orgSlug, projectId } = request.params as { orgSlug: string; projectId: string };
      const auth = await requireProjectPermission(request, reply, guardDeps, {
        orgSlug,
        projectId,
        action: "collaborator.manage",
        allowOrgAdminOversight: false,
      });
      if (auth === null) return;
      const body = request.body as {
        teamId?: unknown;
        teamSlug?: unknown;
        role?: unknown;
      };
      const role = parseRole(body.role);
      if (role === null) {
        return reply.status(400).send({
          error: { code: "bad_request", message: "role must be READ|EDIT|MANAGE" },
        });
      }
      const entry = await grantTeamProjectAccess(config.prisma, {
        projectId,
        role,
        createdByUserId: auth.user.id,
        ...(typeof body.teamId === "string" ? { teamId: body.teamId } : {}),
        ...(typeof body.teamSlug === "string" ? { teamSlug: body.teamSlug } : {}),
      });
      return entry;
    } catch (err) {
      return handleRouteError(reply, err);
    }
  });

  app.delete(
    "/api/org/:orgSlug/projects/:projectId/team-access/:teamId",
    async (request, reply) => {
      try {
        const { orgSlug, projectId, teamId } = request.params as {
          orgSlug: string;
          projectId: string;
          teamId: string;
        };
        const auth = await requireProjectPermission(request, reply, guardDeps, {
          orgSlug,
          projectId,
          action: "collaborator.manage",
          allowOrgAdminOversight: false,
        });
        if (auth === null) return;
        const count = await revokeTeamProjectAccess(config.prisma, { projectId, teamId });
        return { revoked: count };
      } catch (err) {
        return handleRouteError(reply, err);
      }
    },
  );
}

function parseRole(value: unknown): PermissionRole | null {
  if (typeof value !== "string") return null;
  return (ROLES as readonly string[]).includes(value) ? (value as PermissionRole) : null;
}
