import { createResolver, t } from "@tailor-platform/sdk";
import { createContext } from "@tailor-platform/erp-kit/app";
import { getDB } from "@/generated/kysely-tailordb";
import { umModules, auditModules } from "@/modules";

const AUTO_ASSIGNED_SCOPES = ["audit:auditEvent", "user-management:profile"];

export default createResolver({
  name: "updateRole",
  operation: "mutation",
  input: {
    roleId: t.string().description("Role ID"),
    name: t.string({ optional: true }).description("New role name"),
    description: t.string({ optional: true }).description("New role description"),
    permissions: t.string({ array: true }).description("Desired permission keys"),
  },
  body: async (context) => {
    const db = getDB("main-db");
    const allPermissions = [...new Set([...context.input.permissions, ...AUTO_ASSIGNED_SCOPES])];

    const result = await db.transaction().execute(async (trx) => {
      const ctx = createContext(context);

      // Capture old values for audit
      const oldRole = await trx
        .selectFrom("Role")
        .selectAll()
        .where("id", "=", context.input.roleId)
        .executeTakeFirst();

      if (!oldRole) {
        throw new Error(`Role ${context.input.roleId} does not exist`);
      }

      const updateInput: {
        id: string;
        name?: string;
        description?: string;
        permissions?: string[];
      } = {
        id: context.input.roleId,
        permissions: allPermissions,
      };
      if (context.input.name != null) updateInput.name = context.input.name;
      if (context.input.description != null) updateInput.description = context.input.description;

      const updateResult = await umModules.commands.updateRole(trx, updateInput, ctx);
      if (!updateResult.ok) {
        switch (updateResult.error.code) {
          case "USER_MANAGEMENT_ROLE_NOT_FOUND":
            throw new Error(`Role ${context.input.roleId} does not exist`);
          case "USER_MANAGEMENT_ROLE_ALREADY_EXISTS":
            throw new Error(`A role with name "${context.input.name}" already exists`);
          case "USER_MANAGEMENT_ROLE_NOT_ACTIVE":
            throw new Error(`Role ${context.input.roleId} is not active`);
          case "USER_MANAGEMENT_MISSING_REQUIRED_FIELD":
            throw new Error("Role name cannot be empty");
          case "INSUFFICIENT_PERMISSION":
            throw updateResult.error;
        }
      }

      return {
        roleId: context.input.roleId,
        oldName: oldRole.name,
        oldDescription: oldRole.description ?? "",
        oldPermissions: (oldRole.permissions ?? []).sort().join(","),
      };
    });

    const changes: { fieldName: string; oldValue: string; newValue: string }[] = [];
    if (context.input.name != null) {
      changes.push({ fieldName: "name", oldValue: result.oldName, newValue: context.input.name });
    }
    if (context.input.description != null) {
      changes.push({
        fieldName: "description",
        oldValue: result.oldDescription,
        newValue: context.input.description,
      });
    }
    changes.push({
      fieldName: "permissions",
      oldValue: result.oldPermissions,
      newValue: allPermissions.sort().join(","),
    });

    await db
      .transaction()
      .execute(async (trx) => {
        await auditModules.commands.logAuditEvent(
          trx,
          {
            eventId: crypto.randomUUID(),
            actorType: "USER",
            actorId: context.user.id,
            entityType: "Role",
            entityId: result.roleId,
            operationType: "UPDATE",
            changes,
          },
          createContext(context),
        );
      })
      .catch(() => undefined);

    return { roleId: result.roleId };
  },
  output: t
    .object({
      roleId: t.string().description("Role ID"),
    })
    .description("Role update result"),
});
