import type { Transaction } from "../generated/kysely-tailordb";
import {
  StockMovementNotFoundError,
  StockMovementNotDraftError,
  EmptyMoveLinesError,
  ItemNotFoundError,
  ItemNotActiveError,
  InvalidQuantityError,
  SameSourceDestinationError,
  StorageLocationNotFoundError,
} from "../lib/errors.generated";
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface ConfirmStockMovementInput {
  id: string;
}

export interface ItemManagementQueries {
  getItem: (
    db: unknown,
    input: { id: string },
    ctx: CommandContext,
  ) => Promise<{ ok: true; value: { item: { id: string; status: string } | null } }>;
}

/**
 * Function: confirmStockMovement
 *
 * Transitions a stock movement from DRAFT to CONFIRMED status.
 * All move lines are revalidated before confirmation to ensure data integrity.
 */
export async function run(
  db: Transaction,
  input: ConfirmStockMovementInput,
  ctx: CommandContext,
  itemManagementQueries: ItemManagementQueries,
) {
  const { id } = input;

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

  if (!movement) {
    return err(new StockMovementNotFoundError(id));
  }

  // 2. Must be DRAFT
  if (movement.status !== "DRAFT") {
    return err(new StockMovementNotDraftError(id));
  }

  // 3. Load and revalidate move lines
  const lines = await db
    .selectFrom("StockMoveLine")
    .selectAll()
    .where("stockMovementId", "=", id)
    .execute();

  if (lines.length === 0) {
    return err(new EmptyMoveLinesError(id));
  }

  for (const line of lines) {
    // Check item exists and is ACTIVE
    const itemResult = await itemManagementQueries.getItem(db, { id: line.itemId }, ctx);
    const item = itemResult.value.item;

    if (!item) {
      return err(new ItemNotFoundError(line.itemId));
    }

    if (item.status !== "ACTIVE") {
      return err(new ItemNotActiveError(line.itemId));
    }

    // Quantity > 0
    if (line.quantity <= 0) {
      return err(new InvalidQuantityError(line.itemId));
    }

    // Source != destination
    if (
      line.sourceLocationId &&
      line.destinationLocationId &&
      line.sourceLocationId === line.destinationLocationId
    ) {
      return err(new SameSourceDestinationError(line.sourceLocationId));
    }

    // Validate locations exist
    if (line.sourceLocationId) {
      const sourceLoc = await db
        .selectFrom("StorageLocation")
        .selectAll()
        .where("id", "=", line.sourceLocationId)
        .executeTakeFirst();
      if (!sourceLoc) {
        return err(new StorageLocationNotFoundError(line.sourceLocationId));
      }
    }

    if (line.destinationLocationId) {
      const destLoc = await db
        .selectFrom("StorageLocation")
        .selectAll()
        .where("id", "=", line.destinationLocationId)
        .executeTakeFirst();
      if (!destLoc) {
        return err(new StorageLocationNotFoundError(line.destinationLocationId));
      }
    }
  }

  // 4. Update status to CONFIRMED
  const confirmed = await db
    .updateTable("StockMovement")
    .set({
      status: "CONFIRMED",
      updatedAt: new Date(),
    })
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirst();

  return ok({ stockMovement: confirmed! });
}
