import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { defineModule as definePrimitivesModule } from "../../primitives";
import type { Transaction, Updateable } from "../generated/kysely-tailordb";
import {
  PartnerNotFoundError,
  InvalidNameError,
  CurrencyNotFoundError,
} from "../lib/errors.generated";
type PrimitivesModule = ReturnType<typeof definePrimitivesModule>;
type PrimitivesQueries = PrimitivesModule["queries"];

export type UpdatePartnerInput = {
  id: string;
  name?: string;
  preferredCurrencyId?: string | null;
};
/**
 * Function: updatePartner
 *
 * Updates mutable fields of an existing business partner.
 * Validates partner existence, name non-empty if provided, and currency if provided.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdatePartnerInput & Omit<Partial<CF>, "status">,
  ctx: CommandContext,
  primitivesQueries: Pick<PrimitivesQueries, "getCurrency">,
) {
  const { id, name, preferredCurrencyId } = input;

  const KNOWN_KEYS = new Set(["id", "name", "preferredCurrencyId"]);
  const customFields: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
    if (!KNOWN_KEYS.has(key)) {
      customFields[key] = value;
    }
  }

  // 1. Check partner exists
  const partner = await db
    .selectFrom("BusinessPartner")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!partner) {
    return err(new PartnerNotFoundError(id));
  }

  // 2. Validate name is non-empty if provided
  if (name?.trim() === "") {
    return err(new InvalidNameError(id));
  }

  // 3. Validate currency exists if provided (and non-null)
  if (preferredCurrencyId !== undefined && preferredCurrencyId !== null) {
    const { currency } = (await primitivesQueries.getCurrency(db, { id: preferredCurrencyId }, ctx))
      .value;
    if (!currency) {
      return err(new CurrencyNotFoundError(preferredCurrencyId));
    }
  }

  // 4. Apply updates
  const updates: Updateable<"BusinessPartner"> = {
    ...(customFields as Updateable<"BusinessPartner">),
  };
  if (name !== undefined) updates.name = name;
  if (preferredCurrencyId !== undefined) updates.preferredCurrencyId = preferredCurrencyId;

  if (Object.keys(updates).length === 0) {
    return ok({ partner });
  }

  const updatedPartner = await db
    .updateTable("BusinessPartner")
    .set(updates)
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ partner: updatedPartner });
}
