import type { Transaction } from "../generated/kysely-tailordb";
import {
  IdentificationNotFoundError,
  InvalidValueError,
  DuplicateIdentificationError,
  InvalidValidityPeriodError,
} from "../lib/errors.generated";
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";

export type UpdatePartnerIdentificationInput = {
  id: string;
  value?: string;
  issuingCountry?: string;
  validFrom?: Date;
  validTo?: Date;
};
/**
 * Function: updatePartnerIdentification
 *
 * Updates an existing partner identification record.
 * Type is immutable. Re-validates (type, value) uniqueness if value changes.
 * Validates validity period using merged result with existing data.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdatePartnerIdentificationInput & Omit<Partial<CF>, "status">,
  _ctx: CommandContext,
) {
  const { id, value, issuingCountry, validFrom, validTo, ...customFields } = input;

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

  if (!existing) {
    return err(new IdentificationNotFoundError(id));
  }

  // 2. Validate value is non-empty if provided
  if (value !== undefined && (!value || value.trim() === "")) {
    return err(new InvalidValueError(value ?? ""));
  }

  // 3. If value changed, re-validate (type, value) uniqueness within company scope
  if (value !== undefined && value !== existing.value) {
    // Get the partner to find companyId
    const partner = await db
      .selectFrom("BusinessPartner")
      .selectAll()
      .where("id", "=", existing.partnerId)
      .executeTakeFirst();

    const duplicate = await db
      .selectFrom("PartnerIdentification")
      .innerJoin("BusinessPartner", "BusinessPartner.id", "PartnerIdentification.partnerId")
      .selectAll("PartnerIdentification")
      .where("BusinessPartner.companyId", "=", partner!.companyId)
      .where("PartnerIdentification.type", "=", existing.type)
      .where("PartnerIdentification.value", "=", value)
      .executeTakeFirst();

    if (duplicate) {
      return err(new DuplicateIdentificationError(`${existing.type}:${value}`));
    }
  }

  // 4. Validate validity period (merge with existing)
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- null means "clear the field", distinct from undefined (not provided)
  const effectiveFrom = validFrom !== undefined ? validFrom : existing.validFrom;
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
  const effectiveTo = validTo !== undefined ? validTo : existing.validTo;

  if (effectiveFrom && effectiveTo && effectiveTo < effectiveFrom) {
    return err(
      new InvalidValidityPeriodError(
        `${effectiveFrom instanceof Date ? effectiveFrom.toISOString() : effectiveFrom} - ${effectiveTo instanceof Date ? effectiveTo.toISOString() : effectiveTo}`,
      ),
    );
  }

  // 5. Build update set
  const updateSet: Record<string, unknown> = {
    ...customFields,
    updatedAt: new Date(),
  };
  if (value !== undefined) updateSet.value = value;
  if (issuingCountry !== undefined) updateSet.issuingCountry = issuingCountry;
  if (validFrom !== undefined) updateSet.validFrom = validFrom;
  if (validTo !== undefined) updateSet.validTo = validTo;

  // 6. Update record
  const identification = await db
    .updateTable("PartnerIdentification")
    .set(updateSet)
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ identification });
}
