import type { Transaction } from "../generated/kysely-tailordb";
import {
  ItemNotFoundError,
  StorageLocationNotFoundError,
  ReasonCodeRequiredError,
  ScrapQuantityMustBeNegativeError,
  LotNotFoundError,
  SerialNumberNotFoundError,
} from "../lib/errors.generated";
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";

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

export interface CreateInventoryAdjustmentInput {
  adjustmentType?: "CORRECTION" | "SCRAP";
  itemId: string;
  storageLocationId: string;
  quantityDelta: number;
  reasonCode?: string;
  inventoryCountId?: string;
  lotId?: string;
  serialNumberId?: string;
}

/**
 * Function: createInventoryAdjustment
 *
 * Creates a new inventory adjustment record in DRAFT status.
 * CORRECTION: Manual corrections to on-hand quantities (positive or negative).
 * SCRAP: Controlled disposal of unusable stock (must be negative).
 */
export async function run(
  db: Transaction,
  input: CreateInventoryAdjustmentInput,
  ctx: CommandContext,
  itemManagementQueries: ItemManagementQueries,
) {
  const {
    adjustmentType = "CORRECTION",
    itemId,
    storageLocationId,
    quantityDelta,
    reasonCode,
    inventoryCountId,
    lotId,
    serialNumberId,
  } = input;

  // 1. Validate item exists
  const itemResult = await itemManagementQueries.getItem(db, { id: itemId }, ctx);
  if (!itemResult.value.item) {
    return err(new ItemNotFoundError(itemId));
  }

  // 2. Validate storage location exists
  const location = await db
    .selectFrom("StorageLocation")
    .selectAll()
    .where("id", "=", storageLocationId)
    .executeTakeFirst();

  if (!location) {
    return err(new StorageLocationNotFoundError(storageLocationId));
  }

  // 3. Validate reason code provided
  if (!reasonCode) {
    return err(new ReasonCodeRequiredError(itemId));
  }

  // 4. For SCRAP, quantityDelta must be negative
  if (adjustmentType === "SCRAP" && quantityDelta >= 0) {
    return err(new ScrapQuantityMustBeNegativeError(itemId));
  }

  // 5. Validate lot exists if provided
  if (lotId) {
    const lot = await db.selectFrom("Lot").selectAll().where("id", "=", lotId).executeTakeFirst();

    if (!lot) {
      return err(new LotNotFoundError(lotId));
    }
  }

  // 6. Validate serial number exists if provided
  if (serialNumberId) {
    const serial = await db
      .selectFrom("SerialNumber")
      .selectAll()
      .where("id", "=", serialNumberId)
      .executeTakeFirst();

    if (!serial) {
      return err(new SerialNumberNotFoundError(serialNumberId));
    }
  }

  // 7. Create adjustment in DRAFT status
  const adjustment = await db
    .insertInto("InventoryAdjustment")
    .values({
      adjustmentType,
      itemId,
      storageLocationId,
      quantityDelta,
      reasonCode,
      status: "DRAFT",
      inventoryCountId: inventoryCountId ?? null,
      lotId: lotId ?? null,
      serialNumberId: serialNumberId ?? null,
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  return ok({ inventoryAdjustment: adjustment! });
}
