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

const PASSWORD_LENGTH = 16;
const PASSWORD_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*";

function generatePassword(): string {
  const bytes = new Uint8Array(PASSWORD_LENGTH);
  crypto.getRandomValues(bytes);
  return Array.from(bytes, (b) => PASSWORD_CHARS[b % PASSWORD_CHARS.length]).join("");
}

export default createResolver({
  name: "createUser",
  operation: "mutation",
  input: {
    name: t.string().description("User name"),
    email: t.string().description("User email"),
    roleIds: t.uuid({ array: true, optional: true }).description("Role IDs to assign"),
  },
  body: async (context) => {
    const db = getDB("main-db");
    const password = generatePassword();
    const roleIds = context.input.roleIds ?? [];
    const result = await db.transaction().execute(async (trx) => {
      const createResult = await umModules.commands.createUser(
        trx,
        {
          email: context.input.email,
          name: context.input.name,
        },
        createContext(context),
      );
      if (!createResult.ok) {
        switch (createResult.error.code) {
          case "USER_MANAGEMENT_USER_ALREADY_EXISTS":
            throw new Error(`A user with email ${context.input.email} already exists`);
          case "USER_MANAGEMENT_MISSING_REQUIRED_FIELD":
            throw new Error(`Missing required field: ${createResult.error.message}`);
          case "USER_MANAGEMENT_INVALID_EMAIL":
            throw new Error(`Invalid email format: ${context.input.email}`);
          case "INSUFFICIENT_PERMISSION":
            throw createResult.error;
          default:
            throw createResult.error satisfies never;
        }
      }

      const activateResult = await umModules.commands.activateUser(
        trx,
        { userId: createResult.value.user.id },
        createContext(context),
      );
      if (!activateResult.ok) throw activateResult.error;

      for (const roleId of roleIds) {
        const assignResult = await umModules.commands.assignRoleToUser(
          trx,
          {
            userId: createResult.value.user.id,
            roleId,
          },
          createContext(context),
        );
        if (!assignResult.ok) {
          switch (assignResult.error.code) {
            case "USER_MANAGEMENT_ROLE_NOT_FOUND":
              throw new Error(`Role ${roleId} does not exist`);
            case "USER_MANAGEMENT_USER_NOT_FOUND":
              throw new Error(`User does not exist`);
            case "USER_MANAGEMENT_USER_NOT_ACTIVE":
              throw new Error("User is not in ACTIVE status");
            case "USER_MANAGEMENT_ROLE_NOT_ACTIVE":
              throw new Error(`Role ${roleId} is not active`);
            case "INSUFFICIENT_PERMISSION":
              throw assignResult.error;
            default:
              throw assignResult.error satisfies never;
          }
        }
      }

      // IDP failure rolls back the entire transaction, preventing orphaned DB records
      const idpClient = new tailor.idp.Client({ namespace: "default" });
      await idpClient.createUser({ name: context.input.email, password });

      return {
        id: createResult.value.user.id,
        name: createResult.value.user.name,
        email: createResult.value.user.email,
        status: activateResult.value.user.status,
      };
    });

    return { ...result, initialPassword: password };
  },
  output: t
    .object({
      id: t.uuid().description("User ID"),
      name: t.string().description("User name"),
      email: t.string().description("User email"),
      status: t.string().description("User status"),
      initialPassword: t.string().description("Auto-generated initial password"),
    })
    .description("Created user"),
});
