import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import { manufacturingCostSummaryLifecycle } from "../db/manufacturingCostSummary.lifecycle.generated";
import { CostVarianceLineVarianceType } from "../generated/enums";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  CostSummaryNotFoundError,
  CostSummaryNotReviewableError,
  ParentOrderNotTechnicallyCompleteError,
  IncompleteVarianceBreakdownError,
  ReviewerRequiredError,
  VarianceCalculationFailedError,
} from "../lib/errors.generated";

export interface VarianceBreakdownEntry {
  varianceType: CostVarianceLineVarianceType;
  amount: number;
  accountReference?: string | null;
  variancePercentage?: number | null;
}

export interface ReviewManufacturingCostSummaryInput {
  costSummaryId: string;
  reviewerId?: string | null;
  reviewerNotes?: string | null;
  varianceBreakdown?: VarianceBreakdownEntry[] | null;
}

/**
 * Function: reviewManufacturingCostSummary
 *
 * Recalculates the final planned-versus-actual result for a technically
 * complete order and freezes the variance classification through an
 * explicit reviewer approval step.
 */
export async function run(
  db: Transaction,
  input: ReviewManufacturingCostSummaryInput,
  _ctx: CommandContext,
) {
  const { costSummaryId, reviewerId, reviewerNotes, varianceBreakdown } = input;

  // 1. Validate reviewer identity
  if (!reviewerId) {
    return err(new ReviewerRequiredError(costSummaryId));
  }

  // 2. Resolve cost summary
  const costSummary = await db
    .selectFrom("ManufacturingCostSummary")
    .selectAll()
    .where("id", "=", costSummaryId)
    .forUpdate()
    .executeTakeFirst();

  if (!costSummary) {
    return err(new CostSummaryNotFoundError(costSummaryId));
  }

  // 3. Summary must be PENDING_VARIANCE_REVIEW
  const nextStatus = manufacturingCostSummaryLifecycle.tryTransition(costSummary.status, "review");
  if (!nextStatus) {
    return err(new CostSummaryNotReviewableError(costSummaryId));
  }

  // 4. Parent production order must be TECHNICALLY_COMPLETE
  const productionOrder = await db
    .selectFrom("ProductionOrder")
    .selectAll()
    .where("id", "=", costSummary.productionOrderId)
    .forUpdate()
    .executeTakeFirst();

  if (productionOrder?.status !== "TECHNICALLY_COMPLETE") {
    return err(new ParentOrderNotTechnicallyCompleteError(costSummaryId));
  }

  // 5. Validate variance breakdown covers all required categories
  if (!varianceBreakdown || !Array.isArray(varianceBreakdown)) {
    return err(new IncompleteVarianceBreakdownError(costSummaryId));
  }

  const providedTypes = new Set(varianceBreakdown.map((v) => v.varianceType));
  const missingTypes = Object.values(CostVarianceLineVarianceType).filter(
    (t) => !providedTypes.has(t),
  );

  if (missingTypes.length > 0) {
    return err(new IncompleteVarianceBreakdownError(costSummaryId));
  }

  // 6. Validate that variance amounts are consistent (no NaN or undefined)
  for (const entry of varianceBreakdown) {
    if (typeof entry.amount !== "number" || isNaN(entry.amount)) {
      return err(new VarianceCalculationFailedError(costSummaryId));
    }
  }

  // 7. Create cost variance lines for each breakdown entry (bulk insert)
  if (varianceBreakdown.length > 0) {
    await db
      .insertInto("CostVarianceLine")
      .values(
        varianceBreakdown.map((entry) => ({
          costSummaryId: costSummary.id,
          varianceType: entry.varianceType,
          amount: entry.amount,
          accountReference: entry.accountReference ?? null,
          variancePercentage: entry.variancePercentage ?? null,
        })),
      )
      .execute();
  }

  // 8. Update summary to VARIANCE_REVIEWED
  const updatedSummary = await db
    .updateTable("ManufacturingCostSummary")
    .set({
      status: nextStatus,
      reviewedDate: new Date(),
      reviewerNotes: reviewerNotes ?? null,
    })
    .where("id", "=", costSummary.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ manufacturingCostSummary: updatedSummary });
}
