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

export default createResolver({
  name: "updateOwnProfile",
  operation: "mutation",
  input: {
    name: t.string({ optional: true }).description("New display name"),
    email: t.string({ optional: true }).description("New email address"),
  },
  body: async (context) => {
    const db = getDB("main-db");
    let oldEmail: string | undefined;

    const result = await db.transaction().execute(async (trx) => {
      const oldUser = await trx
        .selectFrom("User")
        .select(["email"])
        .where("id", "=", context.user.id)
        .executeTakeFirst();
      if (oldUser) {
        oldEmail = oldUser.email;
      }

      const cmdResult = await umModules.commands.updateOwnProfile(
        trx,
        {
          name: context.input.name ?? undefined,
          email: context.input.email ?? undefined,
        },
        createContext(context),
      );
      if (!cmdResult.ok) {
        switch (cmdResult.error.code) {
          case "USER_MANAGEMENT_USER_NOT_FOUND":
            throw new Error("User not found");
          case "USER_MANAGEMENT_INVALID_STATUS_TRANSITION":
            throw new Error("Only active users can update their own profile");
          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("At least one of name or email must be provided");
          case "USER_MANAGEMENT_INVALID_NAME":
            throw new Error("Name must not be empty");
          case "USER_MANAGEMENT_INVALID_EMAIL":
            throw new Error(`Invalid email format: ${context.input.email}`);
          case "INSUFFICIENT_PERMISSION":
            throw cmdResult.error;
          default:
            throw cmdResult.error satisfies never;
        }
      }

      // IDP uses email as the username — sync when email changes
      if (context.input.email != null && context.input.email !== oldEmail) {
        const idpClient = new tailor.idp.Client({ namespace: "default" });
        const idpUser = (await idpClient.users({ query: { names: [oldEmail!] } })).users[0];
        if (idpUser) {
          await idpClient.updateUser({ id: idpUser.id, name: context.input.email });
        }
      }

      return {
        id: cmdResult.value.user.id,
        name: cmdResult.value.user.name,
        email: cmdResult.value.user.email,
      };
    });

    const changes: Parameters<typeof auditModules.commands.logAuditEvent>[1]["changes"] = [];
    if (context.input.name != null) {
      changes.push({ fieldName: "name", newValue: result.name });
    }
    if (context.input.email != null) {
      changes.push({ fieldName: "email", newValue: result.email });
    }
    await db
      .transaction()
      .execute(async (trx) => {
        await auditModules.commands.logAuditEvent(
          trx,
          {
            eventId: crypto.randomUUID(),
            actorType: "USER",
            actorId: createContext(context).actorId,
            entityType: "User",
            entityId: result.id,
            operationType: "UPDATE",
            changes,
          },
          createContext(context),
        );
      })
      .catch(() => undefined);

    return result;
  },
  output: t
    .object({
      id: t.uuid().description("User ID"),
      name: t.string().description("Updated name"),
      email: t.string().description("Updated email"),
    })
    .description("Updated profile"),
});
