import type { Transaction } from "../generated/kysely-tailordb";
import {
  InvalidScopeError,
  DuplicateWorkCenterCodeError,
  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 interface CreateWorkCenterInput {
  code: string;
  companyId: string;
  siteId?: string | null;
  capacityAssumptions: number;
  laborRate?: number | null;
  machineRate?: number | null;
  calendarReference?: string | null;
  overheadAbsorptionMethod?: string | null;
  overheadAbsorptionCurrency?: string | null;
}

/**
 * Function: createWorkCenter
 *
 * Creates a new work center in DRAFT status with scope, capacity assumptions,
 * rate context, and optional overhead-absorption policy.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreateWorkCenterInput & CF,
  _ctx: CommandContext,
) {
  const {
    code,
    companyId,
    siteId,
    capacityAssumptions,
    laborRate,
    machineRate,
    calendarReference,
    overheadAbsorptionMethod,
    overheadAbsorptionCurrency,
    ...customFields
  } = input;

  // 1. Validate scope — company is always required
  if (!companyId) {
    return err(new InvalidScopeError(code));
  }

  // 2. Validate capacity > 0
  if (capacityAssumptions <= 0) {
    return err(new InvalidCapacityError(code));
  }

  // 3. Validate rates >= 0
  if (laborRate != null && laborRate < 0) {
    return err(new InvalidRateError(code));
  }
  if (machineRate != null && machineRate < 0) {
    return err(new InvalidRateError(code));
  }

  // 4. Validate overhead method
  if (
    overheadAbsorptionMethod != null &&
    !VALID_OVERHEAD_METHODS.includes(
      overheadAbsorptionMethod as (typeof VALID_OVERHEAD_METHODS)[number],
    )
  ) {
    return err(new InvalidOverheadMethodError(code));
  }

  // 5. Overhead currency required for FIXED_AMOUNT_PER_GOOD_UNIT
  if (overheadAbsorptionMethod === "FIXED_AMOUNT_PER_GOOD_UNIT" && !overheadAbsorptionCurrency) {
    return err(new OverheadCurrencyRequiredError(code));
  }

  // 6. Check code uniqueness within company+site scope
  let existingQuery = db
    .selectFrom("WorkCenter")
    .selectAll()
    .where("code", "=", code)
    .where("companyId", "=", companyId);

  if (siteId) {
    existingQuery = existingQuery.where("siteId", "=", siteId);
  }

  const existing = await existingQuery.forUpdate().executeTakeFirst();

  if (existing) {
    return err(new DuplicateWorkCenterCodeError(code));
  }

  // 7. Create work center in DRAFT status
  const workCenter = await db
    .insertInto("WorkCenter")
    .values({
      ...(customFields as Record<string, unknown>),
      code,
      companyId,
      siteId: siteId ?? null,
      capacityAssumptions,
      laborRate: laborRate ?? null,
      machineRate: machineRate ?? null,
      calendarReference: calendarReference ?? null,
      overheadAbsorptionMethod:
        (overheadAbsorptionMethod as
          | "PERCENT_OF_LABOR_COST"
          | "PERCENT_OF_MACHINE_COST"
          | "FIXED_AMOUNT_PER_GOOD_UNIT"
          | null) ?? null,
      overheadAbsorptionCurrency: overheadAbsorptionCurrency ?? null,
      status: "DRAFT",
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  return ok({ workCenter: workCenter! });
}
