import { Transaction } from "../generated/kysely-tailordb";
import {
  RoleNotFoundError,
  RoleNotActiveError,
  MissingRequiredFieldError,
  RoleAlreadyExistsError,
  InvalidPermissionError,
} from "../lib/errors.generated";
import { validatePermissionKey } from "../lib/validatePermissionKey";
import { ok, err } from "@tailor-platform/erp-kit/core";

export type UpdateRoleInput = { id: string } & {
  name?: string;
  description?: string;
  permissions?: string[];
};

/**
 * Function: updateRole
 *
 * Updates an existing role's name or description.
 * Only ACTIVE roles can be updated. Existing UserRole and RolePermission associations remain intact.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateRoleInput & Omit<Partial<CF>, "status">,
) {
  const { id, name, description, permissions, ...customFields } = input;

  const role = await db
    .selectFrom("Role")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!role) {
    return err(new RoleNotFoundError(id));
  }

  if (role.status !== "ACTIVE") {
    return err(new RoleNotActiveError(id));
  }

  if (name?.trim() === "") {
    return err(new MissingRequiredFieldError("name"));
  }

  if (permissions) {
    for (const key of permissions) {
      if (!validatePermissionKey(key)) {
        return err(new InvalidPermissionError(key));
      }
    }
  }

  if (name !== undefined && name !== role.name) {
    const existingRole = await db
      .selectFrom("Role")
      .selectAll()
      .where("name", "=", name)
      .forUpdate()
      .executeTakeFirst();

    if (existingRole) {
      return err(new RoleAlreadyExistsError(name));
    }
  }

  const updatedRole = await db
    .updateTable("Role")
    .set({
      ...(customFields as Record<string, unknown>),
      ...(name !== undefined ? { name } : {}),
      ...(description !== undefined ? { description } : {}),
      ...(permissions !== undefined ? { permissions } : {}),
      updatedAt: new Date(),
    })
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirst();

  return ok({ role: updatedRole! });
}
