import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { AccountAccountType } from "../generated/enums";
import type { Transaction, Updateable } from "../generated/kysely-tailordb";
import {
  AccountNotFoundError,
  NameRequiredError,
  InvalidAccountCodeError,
  DuplicateAccountCodeError,
} from "../lib/errors.generated";

export type UpdateAccountInput = {
  id: string;
  name?: string;
  code?: string;
  accountType?: AccountAccountType;
};

/**
 * Function: updateAccount
 *
 * Modifies the core fields of an existing GL account.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateAccountInput & Omit<Partial<CF>, "status">,
  _ctx: CommandContext,
) {
  const { id, name, code, accountType, ...customFields } = input;

  // 1. Find account with forUpdate
  const account = await db
    .selectFrom("Account")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!account) {
    return err(new AccountNotFoundError(id));
  }

  // 2. Validate name if provided
  if (name?.trim() === "") {
    return err(new NameRequiredError(id));
  }

  // 3. Validate code if changed
  if (code !== undefined && code !== account.code) {
    if (!/^\d+$/.test(code)) {
      return err(new InvalidAccountCodeError(code));
    }

    const existingAccount = await db
      .selectFrom("Account")
      .selectAll()
      .where("companyId", "=", account.companyId)
      .where("code", "=", code)
      .executeTakeFirst();

    if (existingAccount && existingAccount.id !== id) {
      return err(new DuplicateAccountCodeError(code));
    }
  }

  // 4. Build update data
  const RESERVED_KEYS = new Set([
    "id",
    "companyId",
    "name",
    "code",
    "accountType",
    "status",
    "createdAt",
    "updatedAt",
  ]);
  const safeCustomFields: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(customFields as Record<string, unknown>)) {
    if (!RESERVED_KEYS.has(key)) {
      safeCustomFields[key] = value;
    }
  }

  const updateData: Updateable<"Account"> = {
    ...(safeCustomFields as Updateable<"Account">),
  };

  if (name !== undefined) updateData.name = name;
  if (code !== undefined) updateData.code = code;
  if (accountType !== undefined) updateData.accountType = accountType;

  if (Object.keys(updateData).length === 0) {
    return ok({ account });
  }

  const updated = await db
    .updateTable("Account")
    .set(updateData)
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ account: updated });
}
