import type { OrganizationMemberRole, PrismaClient } from "@prisma/client";
import type { FastifyInstance } from "fastify";
import {
  addOrgMember,
  listOrgMembers,
  revokeOrgMember,
  setOrgMemberRole,
} from "../../org/members.js";
import { requireOrgRole, type GuardDeps } from "../auth/guards.js";
import { handleRouteError } from "../errors.js";

const ROLES: readonly OrganizationMemberRole[] = ["OWNER", "ADMIN", "MEMBER"];

export async function registerMembersRoutes(
  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/members", async (request, reply) => {
    try {
      const { orgSlug } = request.params as { orgSlug: string };
      const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
      if (auth === null) return;
      return { members: await listOrgMembers(config.prisma, auth.organization.id) };
    } catch (err) {
      return handleRouteError(reply, err);
    }
  });

  app.post("/api/org/:orgSlug/members", async (request, reply) => {
    try {
      const { orgSlug } = request.params as { orgSlug: string };
      const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
      if (auth === null) return;
      const body = request.body as {
        feishuOpenId?: unknown;
        displayName?: unknown;
        role?: unknown;
      };
      if (typeof body.feishuOpenId !== "string") {
        return reply.status(400).send({
          error: { code: "bad_request", message: "feishuOpenId is required" },
        });
      }
      const role = parseRole(body.role) ?? "MEMBER";
      const member = await addOrgMember(config.prisma, {
        organizationId: auth.organization.id,
        actorRole: auth.membershipRole,
        feishuOpenId: body.feishuOpenId,
        role,
        ...(typeof body.displayName === "string" ? { displayName: body.displayName } : {}),
      });
      return reply.status(201).send(member);
    } catch (err) {
      return handleRouteError(reply, err);
    }
  });

  app.patch("/api/org/:orgSlug/members/:userId", async (request, reply) => {
    try {
      const { orgSlug, userId } = request.params as { orgSlug: string; userId: string };
      const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
      if (auth === null) return;
      const body = request.body as { role?: unknown };
      const role = parseRole(body.role);
      if (role === null) {
        return reply.status(400).send({
          error: { code: "bad_request", message: "role must be OWNER|ADMIN|MEMBER" },
        });
      }
      return await setOrgMemberRole(config.prisma, {
        organizationId: auth.organization.id,
        actorUserId: auth.user.id,
        actorRole: auth.membershipRole,
        targetUserId: userId,
        role,
      });
    } catch (err) {
      return handleRouteError(reply, err);
    }
  });

  app.post("/api/org/:orgSlug/members/:userId/revoke", async (request, reply) => {
    try {
      const { orgSlug, userId } = request.params as { orgSlug: string; userId: string };
      const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
      if (auth === null) return;
      return await revokeOrgMember(config.prisma, {
        organizationId: auth.organization.id,
        actorUserId: auth.user.id,
        actorRole: auth.membershipRole,
        targetUserId: userId,
      });
    } catch (err) {
      return handleRouteError(reply, err);
    }
  });
}

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