import type { Transaction } from "../generated/kysely-tailordb";
import {
  WorkCenterNotFoundError,
  InvalidCapacityError,
  InvalidRateError,
  InvalidOverheadMethodError,
  OverheadCurrencyRequiredError,
} from "../lib/errors.generated";
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";

const VALID_OVERHEAD_METHODS = [
  "PERCENT_OF_LABOR_COST",
  "PERCENT_OF_MACHINE_COST",
  "FIXED_AMOUNT_PER_GOOD_UNIT",
] as const;

export type UpdateWorkCenterInput = (
  | { id: string }
  | { companyId: string; siteId: string; code: string }
) & {
  capacityAssumptions?: number | null;
  laborRate?: number | null;
  machineRate?: number | null;
  calendarReference?: string | null;
  overheadAbsorptionMethod?: string | null;
  overheadAbsorptionCurrency?: string | null;
};
/**
 * Function: updateWorkCenter
 *
 * Revises mutable work-center master data such as capacity, calendar,
 * rate assumptions, and overhead policy. Only DRAFT or ACTIVE work centers
 * may be updated.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateWorkCenterInput & Omit<Partial<CF>, "status">,
  _ctx: CommandContext,
) {
  const {
    capacityAssumptions,
    laborRate,
    machineRate,
    calendarReference,
    overheadAbsorptionMethod,
    overheadAbsorptionCurrency,
    ...customFields
  } = input;
  delete (customFields as Record<string, unknown>).id;
  delete (customFields as Record<string, unknown>).companyId;
  delete (customFields as Record<string, unknown>).siteId;
  delete (customFields as Record<string, unknown>).code;

  // 1. Check work center exists
  let workCenterQuery = db.selectFrom("WorkCenter").selectAll();
  if ("id" in input) {
    workCenterQuery = workCenterQuery.where("id", "=", (input as { id: string }).id);
  } else {
    const lookup = input as { companyId: string; siteId: string; code: string };
    workCenterQuery = workCenterQuery
      .where("companyId", "=", lookup.companyId)
      .where("siteId", "=", lookup.siteId)
      .where("code", "=", lookup.code);
  }
  const workCenter = await workCenterQuery.forUpdate().executeTakeFirst();

  if (!workCenter) {
    const key =
      "id" in input
        ? (input as { id: string }).id
        : `${(input as { companyId: string; siteId: string; code: string }).companyId}:${(input as { companyId: string; siteId: string; code: string }).siteId}:${(input as { companyId: string; siteId: string; code: string }).code}`;
    return err(new WorkCenterNotFoundError(key));
  }

  // 2. Only DRAFT or ACTIVE allowed
  if (workCenter.status !== "DRAFT" && workCenter.status !== "ACTIVE") {
    return err(new WorkCenterNotFoundError(workCenter.id));
  }

  // 3. Validate capacity if provided
  if (capacityAssumptions != null && capacityAssumptions <= 0) {
    return err(new InvalidCapacityError(workCenter.id));
  }

  // 4. Validate rates if provided
  if (laborRate != null && laborRate < 0) {
    return err(new InvalidRateError(workCenter.id));
  }
  if (machineRate != null && machineRate < 0) {
    return err(new InvalidRateError(workCenter.id));
  }

  // Resolve the effective overhead method after update
  const effectiveMethod =
    overheadAbsorptionMethod !== undefined
      ? overheadAbsorptionMethod
      : workCenter.overheadAbsorptionMethod;
  const effectiveCurrency =
    overheadAbsorptionCurrency !== undefined
      ? overheadAbsorptionCurrency
      : workCenter.overheadAbsorptionCurrency;

  // 5. Validate overhead method
  if (
    effectiveMethod != null &&
    !VALID_OVERHEAD_METHODS.includes(effectiveMethod as (typeof VALID_OVERHEAD_METHODS)[number])
  ) {
    return err(new InvalidOverheadMethodError(workCenter.id));
  }

  // 6. Overhead currency required for FIXED_AMOUNT_PER_GOOD_UNIT
  if (effectiveMethod === "FIXED_AMOUNT_PER_GOOD_UNIT" && !effectiveCurrency) {
    return err(new OverheadCurrencyRequiredError(workCenter.id));
  }

  // 7. Build update set
  const updateSet: Record<string, unknown> = {
    ...(customFields as Record<string, unknown>),
    updatedAt: new Date(),
  };

  if (capacityAssumptions !== undefined) updateSet.capacityAssumptions = capacityAssumptions;
  if (laborRate !== undefined) updateSet.laborRate = laborRate;
  if (machineRate !== undefined) updateSet.machineRate = machineRate;
  if (calendarReference !== undefined) updateSet.calendarReference = calendarReference;
  if (overheadAbsorptionMethod !== undefined)
    updateSet.overheadAbsorptionMethod = overheadAbsorptionMethod;
  if (overheadAbsorptionCurrency !== undefined)
    updateSet.overheadAbsorptionCurrency = overheadAbsorptionCurrency;

  // 8. Persist changes
  const updated = await db
    .updateTable("WorkCenter")
    .set(updateSet)
    .where("id", "=", workCenter.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ workCenter: updated });
}
