import type { CommandContext } from "@tailor-platform/erp-kit/core";
import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  StockAdjustmentNotFoundError,
  InvalidStatusError,
  InsufficientOnHandError,
  InsufficientAvailableStockError,
  InsufficientBlockedStockError,
  InvalidSerialStateForScrapError,
} from "../lib/errors.generated";
import { expectOk, expectErr } from "../testing/commandTestUtils";
import {
  baseSubmittedAdjustment,
  baseDraftAdjustment,
  baseConfirmedAdjustment,
  correctionIncreaseLine,
  scrapAvailableLine,
  scrapBlockedLine,
  blockLine,
  unblockLine,
  baseStockLevel,
  baseLotStockLevel,
  baseAvailableSerial,
  baseIssuedSerial,
  baseItemValuation,
} from "../testing/fixtures";
import { run } from "./confirmStockAdjustment";

describe("confirmStockAdjustment", () => {
  const ctx: CommandContext = {
    actorId: "test-actor",
    permissions: ["inventory:inventoryApproval:confirmStockAdjustment"],
  };

  // Helper to build a submitted adjustment with a specific type
  const makeAdjustment = (type: string) => ({
    ...baseSubmittedAdjustment,
    adjustmentType: type,
  });

  // Helper to build lines referencing adjustment-2 (submitted)
  const makeLine = (overrides: Record<string, unknown> = {}) => ({
    ...correctionIncreaseLine,
    stockAdjustmentId: baseSubmittedAdjustment.id,
    ...overrides,
  });

  // --- Error scenarios ---

  it("returns error when stock adjustment does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined); // Adjustment not found

    const result = await run(db, { id: "nonexistent" }, ctx);
    expectErr(result as never, StockAdjustmentNotFoundError);
  });

  it("returns error when stock adjustment is not in SUBMITTED status", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseDraftAdjustment); // DRAFT status

    const result = await run(db, { id: baseDraftAdjustment.id }, ctx);
    expectErr(result as never, InvalidStatusError);
  });

  it("returns error when stock adjustment is not in SUBMITTED status (CONFIRMED)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseConfirmedAdjustment); // CONFIRMED status

    const result = await run(db, { id: baseConfirmedAdjustment.id }, ctx);
    expectErr(result as never, InvalidStatusError);
  });

  // --- CORRECTION INCREASE ---

  it("confirms CORRECTION INCREASE and creates DR ledger entry (increases onHand)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "INCREASE",
      quantity: 5,
      unitCost: 10,
    });
    const ledgerEntry = {
      id: "ledger-1",
      sourceType: "CORRECTION",
      dcIndicator: "DR",
      amount: 50,
    };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel) // StockLevel lookup
      .mockReturnValueOnce(undefined); // ItemValuation (no valuation)
    spies.insert.mockReturnValue(ledgerEntry);
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      stockAdjustment: { status: string };
      inventoryLedgerEntries: { sourceType: string; dcIndicator: string }[];
    }>(result as never);

    expect(value.inventoryLedgerEntries).toHaveLength(1);
    expect(value.inventoryLedgerEntries[0].sourceType).toBe("CORRECTION");
    expect(value.inventoryLedgerEntries[0].dcIndicator).toBe("DR");
    expect(value.stockAdjustment.status).toBe("CONFIRMED");
  });

  it("creates new StockLevel when none exists for CORRECTION INCREASE", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "INCREASE",
      quantity: 5,
    });
    const ledgerEntry = {
      id: "ledger-1",
      sourceType: "CORRECTION",
      dcIndicator: "DR",
      amount: null,
    };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(undefined) // StockLevel not found
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert
      .mockReturnValueOnce(ledgerEntry) // Ledger entry
      .mockReturnValueOnce({ id: "new-stock-level" }); // StockLevel insert
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk(result as never);
    expect(value).toBeDefined();
    // Should have inserted both ledger and stock level
    expect(spies.insert).toHaveBeenCalledTimes(2);
  });

  // --- CORRECTION DECREASE ---

  it("confirms CORRECTION DECREASE and creates CR ledger entry (decreases onHand)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "DECREASE",
      quantity: 5,
      unitCost: 10,
    });
    const ledgerEntry = {
      id: "ledger-1",
      sourceType: "CORRECTION",
      dcIndicator: "CR",
      amount: 50,
    };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel) // StockLevel (onHand=100)
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert.mockReturnValue(ledgerEntry);
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      inventoryLedgerEntries: { sourceType: string; dcIndicator: string }[];
    }>(result as never);

    expect(value.inventoryLedgerEntries).toHaveLength(1);
    expect(value.inventoryLedgerEntries[0].sourceType).toBe("CORRECTION");
    expect(value.inventoryLedgerEntries[0].dcIndicator).toBe("CR");
  });

  it("returns error when on-hand is insufficient for CORRECTION DECREASE", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "DECREASE",
      quantity: 200, // More than onHand (100)
    });

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel); // StockLevel (onHand=100)

    const result = await run(db, { id: adjustment.id }, ctx);
    expectErr(result as never, InsufficientOnHandError);
  });

  // --- SCRAP AVAILABLE ---

  it("confirms SCRAP (AVAILABLE) and creates 1 CR ledger entry (decreases onHand)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const line = makeLine({
      ...scrapAvailableLine,
      stockAdjustmentId: adjustment.id,
      quantity: 3,
      fromStockCategory: "AVAILABLE",
    });
    const ledgerEntry = { id: "ledger-1", sourceType: "SCRAP", dcIndicator: "CR", amount: null };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel) // StockLevel (onHand=100, blocked=5)
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert.mockReturnValue(ledgerEntry);
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      inventoryLedgerEntries: { sourceType: string; dcIndicator: string }[];
    }>(result as never);

    expect(value.inventoryLedgerEntries).toHaveLength(1);
    expect(value.inventoryLedgerEntries[0].sourceType).toBe("SCRAP");
    expect(value.inventoryLedgerEntries[0].dcIndicator).toBe("CR");
  });

  it("returns error when available portion is insufficient for SCRAP (AVAILABLE)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const lowStockLevel = { ...baseStockLevel, onHand: 10, blocked: 8 };
    // available = 10 - 8 = 2, requesting 5
    const line = makeLine({
      fromStockCategory: "AVAILABLE",
      quantity: 5,
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(lowStockLevel);

    const result = await run(db, { id: adjustment.id }, ctx);
    expectErr(result as never, InsufficientOnHandError);
  });

  // --- SCRAP BLOCKED ---

  it("confirms SCRAP (BLOCKED) and creates 2 ledger entries (SCRAP/CR + UNBLOCK/CR)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const line = makeLine({
      ...scrapBlockedLine,
      stockAdjustmentId: adjustment.id,
      quantity: 3,
      fromStockCategory: "BLOCKED",
    });
    const scrapEntry = { id: "ledger-1", sourceType: "SCRAP", dcIndicator: "CR", amount: null };
    const unblockEntry = { id: "ledger-2", sourceType: "UNBLOCK", dcIndicator: "CR", amount: null };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel) // StockLevel (blocked=5 >= 3)
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert.mockReturnValueOnce(scrapEntry).mockReturnValueOnce(unblockEntry);
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      inventoryLedgerEntries: { sourceType: string; dcIndicator: string }[];
    }>(result as never);

    expect(value.inventoryLedgerEntries).toHaveLength(2);
    expect(value.inventoryLedgerEntries[0].sourceType).toBe("SCRAP");
    expect(value.inventoryLedgerEntries[0].dcIndicator).toBe("CR");
    expect(value.inventoryLedgerEntries[1].sourceType).toBe("UNBLOCK");
    expect(value.inventoryLedgerEntries[1].dcIndicator).toBe("CR");
  });

  it("confirms SCRAP (BLOCKED) and decreases both onHand and blocked", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const line = makeLine({
      fromStockCategory: "BLOCKED",
      quantity: 3,
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel) // onHand=100, blocked=5
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert.mockReturnValue({
      id: "ledger",
      sourceType: "SCRAP",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    // Verify set was called (for StockLevel update and adjustment status)
    expect(spies.set).toHaveBeenCalled();
  });

  it("returns error when blocked is insufficient for SCRAP (BLOCKED)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const lowBlockedLevel = { ...baseStockLevel, blocked: 1 };
    const line = makeLine({
      fromStockCategory: "BLOCKED",
      quantity: 5, // More than blocked (1)
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(lowBlockedLevel);

    const result = await run(db, { id: adjustment.id }, ctx);
    expectErr(result as never, InsufficientBlockedStockError);
  });

  // --- SCRAP serial number transitions ---

  it("confirms SCRAP and transitions serial number from AVAILABLE to SCRAPPED", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const line = makeLine({
      fromStockCategory: "AVAILABLE",
      quantity: 1,
      serialNumberId: "serial-1",
    });

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel) // StockLevel (onHand-blocked >= 1)
      .mockReturnValueOnce(undefined) // ItemValuation
      .mockReturnValueOnce(baseAvailableSerial); // SerialNumber (AVAILABLE)
    spies.insert.mockReturnValue({
      id: "ledger-1",
      sourceType: "SCRAP",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    // Verify serial number update happened
    expect(spies.update).toHaveBeenCalled();
  });

  it("confirms SCRAP and transitions serial number from RETURNED to SCRAPPED", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const returnedSerial = { ...baseAvailableSerial, id: "serial-ret", status: "RETURNED" };
    const line = makeLine({
      fromStockCategory: "AVAILABLE",
      quantity: 1,
      serialNumberId: "serial-ret",
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel)
      .mockReturnValueOnce(undefined) // ItemValuation
      .mockReturnValueOnce(returnedSerial); // SerialNumber (RETURNED)
    spies.insert.mockReturnValue({
      id: "ledger-1",
      sourceType: "SCRAP",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    expect(spies.update).toHaveBeenCalled();
  });

  it("rejects SCRAP when serial is in ISSUED or SCRAPPED state", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const issuedSerial = { ...baseIssuedSerial, status: "ISSUED" };
    const line = makeLine({
      fromStockCategory: "AVAILABLE",
      quantity: 1,
      serialNumberId: "serial-2",
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel)
      .mockReturnValueOnce(undefined) // ItemValuation
      .mockReturnValueOnce(issuedSerial); // SerialNumber (ISSUED)
    spies.insert.mockReturnValue({
      id: "ledger-1",
      sourceType: "SCRAP",
      dcIndicator: "CR",
      amount: null,
    });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectErr(result as never, InvalidSerialStateForScrapError);
  });

  // --- BLOCK ---

  it("confirms BLOCK and creates two ledger entries (CR + DR)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("BLOCK");
    const line = makeLine({
      ...blockLine,
      stockAdjustmentId: adjustment.id,
      quantity: 10,
    });
    const blockCR = { id: "ledger-1", sourceType: "BLOCK", dcIndicator: "CR", amount: null };
    const blockDR = { id: "ledger-2", sourceType: "BLOCK", dcIndicator: "DR", amount: null };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel); // StockLevel (onHand=100, reserved=10, blocked=5 => available=85 >= 10)
    spies.insert.mockReturnValueOnce(blockCR).mockReturnValueOnce(blockDR);
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      inventoryLedgerEntries: { sourceType: string; dcIndicator: string }[];
    }>(result as never);

    expect(value.inventoryLedgerEntries).toHaveLength(2);
    expect(value.inventoryLedgerEntries[0].sourceType).toBe("BLOCK");
    expect(value.inventoryLedgerEntries[0].dcIndicator).toBe("CR");
    expect(value.inventoryLedgerEntries[1].sourceType).toBe("BLOCK");
    expect(value.inventoryLedgerEntries[1].dcIndicator).toBe("DR");
  });

  it("confirms BLOCK and increases blocked without changing onHand", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("BLOCK");
    const line = makeLine({ quantity: 10 });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel); // onHand=100, reserved=10, blocked=5
    spies.insert.mockReturnValue({
      id: "ledger",
      sourceType: "BLOCK",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    // set should be called for StockLevel blocked update and status update
    expect(spies.set).toHaveBeenCalled();
  });

  it("returns error when available stock is insufficient for BLOCK", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("BLOCK");
    // available = 100 - 10 - 5 = 85, requesting 100
    const line = makeLine({ quantity: 100 });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel);

    const result = await run(db, { id: adjustment.id }, ctx);
    expectErr(result as never, InsufficientAvailableStockError);
  });

  // --- UNBLOCK ---

  it("confirms UNBLOCK and creates two ledger entries (DR + CR)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("UNBLOCK");
    const line = makeLine({
      ...unblockLine,
      stockAdjustmentId: adjustment.id,
      quantity: 5,
    });
    const unblockDR = { id: "ledger-1", sourceType: "UNBLOCK", dcIndicator: "DR", amount: null };
    const unblockCR = { id: "ledger-2", sourceType: "UNBLOCK", dcIndicator: "CR", amount: null };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel); // StockLevel (blocked=5 >= 5)
    spies.insert.mockReturnValueOnce(unblockDR).mockReturnValueOnce(unblockCR);
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      inventoryLedgerEntries: { sourceType: string; dcIndicator: string }[];
    }>(result as never);

    expect(value.inventoryLedgerEntries).toHaveLength(2);
    expect(value.inventoryLedgerEntries[0].sourceType).toBe("UNBLOCK");
    expect(value.inventoryLedgerEntries[0].dcIndicator).toBe("DR");
    expect(value.inventoryLedgerEntries[1].sourceType).toBe("UNBLOCK");
    expect(value.inventoryLedgerEntries[1].dcIndicator).toBe("CR");
  });

  it("confirms UNBLOCK and decreases blocked without changing onHand", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("UNBLOCK");
    const line = makeLine({ quantity: 5 });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel); // blocked=5
    spies.insert.mockReturnValue({
      id: "ledger",
      sourceType: "UNBLOCK",
      dcIndicator: "DR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    expect(spies.set).toHaveBeenCalled();
  });

  it("returns error when blocked stock is insufficient for UNBLOCK", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("UNBLOCK");
    const lowBlockedLevel = { ...baseStockLevel, blocked: 2 };
    const line = makeLine({ quantity: 5 }); // More than blocked (2)

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(lowBlockedLevel);

    const result = await run(db, { id: adjustment.id }, ctx);
    expectErr(result as never, InsufficientBlockedStockError);
  });

  // --- Valuation ---

  it("triggers valuation DR for CORRECTION INCREASE", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "INCREASE",
      quantity: 10,
      unitCost: 5,
    });
    const ledgerEntry = { id: "ledger-1", sourceType: "CORRECTION", dcIndicator: "DR", amount: 50 };

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel) // StockLevel
      .mockReturnValueOnce(baseItemValuation) // ItemValuation (for applyValuation)
      .mockReturnValueOnce({ id: "policy-1", costingMethod: "AVCO", standardCostRate: null }); // ValuationPolicy
    spies.insert.mockReturnValue(ledgerEntry);
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    // ItemValuation update happened inside applyValuation
    expect(spies.update).toHaveBeenCalled();
  });

  it("triggers valuation CR for CORRECTION DECREASE and SCRAP (both categories)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "DECREASE",
      quantity: 5,
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel) // StockLevel
      .mockReturnValueOnce(baseItemValuation) // ItemValuation
      .mockReturnValueOnce({ id: "policy-1", costingMethod: "AVCO", standardCostRate: null }); // ValuationPolicy
    spies.insert.mockReturnValue({
      id: "ledger-1",
      sourceType: "CORRECTION",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    expect(spies.update).toHaveBeenCalled();
  });

  it("does not trigger valuation for BLOCK and UNBLOCK", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("BLOCK");
    const line = makeLine({ quantity: 5 });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel); // available = 85 >= 5
    spies.insert.mockReturnValue({
      id: "ledger",
      sourceType: "BLOCK",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    // For BLOCK, applyValuation is NOT called, so no extra selects for ItemValuation
    // The select calls should be: adjustment + lines + stockLevel = 3
    expect(spies.select).toHaveBeenCalledTimes(3);
  });

  // --- LotStockLevel ---

  it("updates LotStockLevel onHand when lotId provided on CORRECTION/SCRAP", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "INCREASE",
      quantity: 10,
      lotId: "lot-1",
    });

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line]) // Lines
      .mockReturnValueOnce(baseStockLevel) // StockLevel
      .mockReturnValueOnce(baseLotStockLevel) // LotStockLevel
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert.mockReturnValue({
      id: "ledger-1",
      sourceType: "CORRECTION",
      dcIndicator: "DR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    // update should be called for StockLevel, LotStockLevel, and StockAdjustment status
    expect(spies.update).toHaveBeenCalled();
  });

  it("updates LotStockLevel blocked when lotId provided on BLOCK/UNBLOCK/SCRAP(BLOCKED)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("BLOCK");
    const line = makeLine({
      quantity: 5,
      lotId: "lot-1",
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel) // available = 85 >= 5
      .mockReturnValueOnce(baseLotStockLevel); // LotStockLevel
    spies.insert.mockReturnValue({
      id: "ledger",
      sourceType: "BLOCK",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    expect(spies.update).toHaveBeenCalled();
  });

  it("updates LotStockLevel blocked when lotId provided on UNBLOCK", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("UNBLOCK");
    const lotWithBlocked = { ...baseLotStockLevel, blocked: 10 };
    const line = makeLine({
      quantity: 5,
      lotId: "lot-1",
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel) // blocked=5
      .mockReturnValueOnce(lotWithBlocked); // LotStockLevel
    spies.insert.mockReturnValue({
      id: "ledger",
      sourceType: "UNBLOCK",
      dcIndicator: "DR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    expect(spies.update).toHaveBeenCalled();
  });

  it("updates LotStockLevel onHand and blocked when lotId provided on SCRAP (BLOCKED)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("SCRAP");
    const lotWithBlocked = { ...baseLotStockLevel, onHand: 50, blocked: 10 };
    const line = makeLine({
      fromStockCategory: "BLOCKED",
      quantity: 3,
      lotId: "lot-1",
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel) // blocked=5 >= 3
      .mockReturnValueOnce(undefined) // ItemValuation
      .mockReturnValueOnce(lotWithBlocked); // LotStockLevel
    spies.insert.mockReturnValue({
      id: "ledger",
      sourceType: "SCRAP",
      dcIndicator: "CR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    expectOk(result as never);
    expect(spies.update).toHaveBeenCalled();
  });

  // --- sourceId ---

  it("sets sourceId on ledger entries referencing the StockAdjustment", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "INCREASE",
      quantity: 5,
    });

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel)
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert.mockReturnValue({
      id: "ledger-1",
      sourceType: "CORRECTION",
      dcIndicator: "DR",
      amount: null,
    });
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    await run(db, { id: adjustment.id }, ctx);

    // Verify values was called with sourceId matching the adjustment id
    expect(spies.values).toHaveBeenCalledWith(expect.objectContaining({ sourceId: adjustment.id }));
  });

  // --- Status transition ---

  it("transitions status from SUBMITTED to CONFIRMED", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line = makeLine({
      adjustmentDirection: "INCREASE",
      quantity: 5,
    });
    const confirmedAdjustment = { ...adjustment, status: "CONFIRMED" };

    spies.select
      .mockReturnValueOnce(adjustment)
      .mockReturnValueOnce([line])
      .mockReturnValueOnce(baseStockLevel)
      .mockReturnValueOnce(undefined); // ItemValuation
    spies.insert.mockReturnValue({
      id: "ledger-1",
      sourceType: "CORRECTION",
      dcIndicator: "DR",
      amount: null,
    });
    spies.update.mockReturnValue(confirmedAdjustment);

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      stockAdjustment: { status: string };
    }>(result as never);

    expect(value.stockAdjustment.status).toBe("CONFIRMED");
  });

  // --- Multiple lines ---

  it("processes multiple lines in a single confirmation", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const adjustment = makeAdjustment("CORRECTION");
    const line1 = makeLine({ adjustmentDirection: "INCREASE", quantity: 5 });
    const line2 = makeLine({
      id: "adj-line-2b",
      adjustmentDirection: "INCREASE",
      quantity: 3,
      itemId: "item-2",
    });

    const ledger1 = { id: "ledger-1", sourceType: "CORRECTION", dcIndicator: "DR", amount: null };
    const ledger2 = { id: "ledger-2", sourceType: "CORRECTION", dcIndicator: "DR", amount: null };

    spies.select
      .mockReturnValueOnce(adjustment) // StockAdjustment
      .mockReturnValueOnce([line1, line2]) // Lines
      .mockReturnValueOnce(baseStockLevel) // StockLevel for line1
      .mockReturnValueOnce(undefined) // ItemValuation for line1
      .mockReturnValueOnce(undefined) // StockLevel for line2 (not found)
      .mockReturnValueOnce(undefined); // ItemValuation for line2
    spies.insert
      .mockReturnValueOnce(ledger1) // Ledger for line1
      .mockReturnValueOnce(ledger2) // Ledger for line2
      .mockReturnValueOnce({ id: "new-stock-level" }); // StockLevel insert for line2
    spies.update.mockReturnValue({ ...adjustment, status: "CONFIRMED" });

    const result = await run(db, { id: adjustment.id }, ctx);
    const value = expectOk<{
      inventoryLedgerEntries: { id: string }[];
    }>(result as never);

    expect(value.inventoryLedgerEntries).toHaveLength(2);
  });
});
