import {
  ok,
  type CommandContext,
  type QueryContext,
  type ValueOf,
} from "@tailor-platform/erp-kit/core";
/* oxlint-disable typescript/no-explicit-any, typescript/no-unsafe-assignment, typescript/no-unsafe-return, typescript/require-await */
import { expect } from "vitest";
import type { InventoryCommands, InventoryQueries } from "../module";

export const commandCtx: CommandContext = {
  actorId: "test-actor",
  permissions: ["purchase:*"],
};

export const queryCtx: QueryContext = {
  actorId: "test-actor",
  permissions: ["purchase:*"],
};

interface OkResult<T> {
  ok: true;
  value: T;
}

interface ErrResult {
  ok: false;
  error: unknown;
}

export function expectErr(
  result: OkResult<unknown> | ErrResult,
  errorClass: abstract new (...args: never[]) => Error,
) {
  expect(result.ok).toBe(false);
  if (!result.ok) {
    expect(result.error).toBeInstanceOf(errorClass);
  }
}

export function expectOk<T>(result: OkResult<T> | ErrResult) {
  expect(result.ok).toBe(true);
  if (!result.ok) {
    throw result.error;
  }
  return result.value;
}

export function mockBPQueries(
  overrides: { partner?: any; partnerRole?: any; identifications?: any[] } = {},
) {
  return {
    getPartner: async () => ok({ partner: overrides.partner ?? null }),
    getPartnerRole: async () => ok({ partnerRole: overrides.partnerRole ?? null }),
    listPartnerIdentificationsByPartner: async () =>
      ok({ items: overrides.identifications ?? [], hasNextPage: false }),
  };
}

export function mockItemQueries(overrides: { item?: any } = {}) {
  return {
    getItem: async () => ok({ item: overrides.item ?? null }),
  };
}

export function mockUnitQueries(overrides: { unit?: any } = {}) {
  return {
    getUnit: async () => ok({ unit: overrides.unit ?? null }),
  };
}

type MockInventoryCommands = Pick<
  InventoryCommands,
  "createInventorySupplyPlan" | "updateInventorySupplyPlan" | "closeInventorySupplyPlan"
>;

type MockInventoryQueries = Pick<
  InventoryQueries,
  "getInventorySupplyPlan" | "listInventorySupplyPlans" | "listPurchaseReceiptEvidenceForMatching"
>;

type InventorySupplyPlanValue = ValueOf<
  Awaited<ReturnType<InventoryCommands["updateInventorySupplyPlan"]>>
>["supplyPlan"];

function buildSupplyPlan(input: Partial<InventorySupplyPlanValue> = {}): InventorySupplyPlanValue {
  return {
    id: input.id ?? "supply-plan-1",
    sourceType: input.sourceType ?? "PURCHASE_ORDER",
    sourceId: input.sourceId ?? "purchase-order-1",
    sourceLineId: input.sourceLineId ?? "po-line-1",
    itemId: input.itemId ?? "item-1",
    siteId: input.siteId ?? "site-1",
    expectedQuantity: input.expectedQuantity ?? "10",
    receivedQuantity: input.receivedQuantity ?? "0",
    status: input.status ?? "OPEN",
    expectedDate: input.expectedDate ?? new Date("2024-01-01T00:00:00.000Z"),
    lotId: input.lotId ?? null,
    serialNumberId: input.serialNumberId ?? null,
    createdAt: input.createdAt ?? new Date("2024-01-01T00:00:00.000Z"),
    updatedAt: input.updatedAt ?? null,
  };
}

export function mockInventoryCommands(): MockInventoryCommands {
  const createInventorySupplyPlan: MockInventoryCommands["createInventorySupplyPlan"] = async (
    _db,
    input,
  ) => {
    const toSupplyPlan = (
      supplyPlanInput: (typeof input.supplyPlans)[number],
      index: number,
    ): InventorySupplyPlanValue => ({
      ...buildSupplyPlan({
        id: `supply-plan-${index + 1}`,
        sourceType: input.sourceType,
        sourceId: input.sourceId,
      }),
      ...supplyPlanInput,
    });
    return ok({
      supplyPlans: input.supplyPlans.map(toSupplyPlan),
    });
  };
  const updateInventorySupplyPlan: MockInventoryCommands["updateInventorySupplyPlan"] = async (
    _db,
    input,
  ) =>
    ok({
      supplyPlan: buildSupplyPlan({
        id: input.id,
        sourceType: input.sourceType,
        sourceId: input.sourceId,
        sourceLineId: input.sourceLineId,
        itemId: input.itemId,
        siteId: input.siteId,
        expectedQuantity: input.expectedQuantity,
        lotId: input.lotId,
        serialNumberId: input.serialNumberId,
      }),
    });
  const closeInventorySupplyPlan: MockInventoryCommands["closeInventorySupplyPlan"] = async (
    _db,
    input,
  ) =>
    input.target === "SOURCE_DOCUMENT" || input.target === "SOURCE_LINES"
      ? ok({ supplyPlans: [] })
      : ok({
          supplyPlan: buildSupplyPlan({
            id: input.id,
            status: "CLOSED",
          }),
        });

  return {
    createInventorySupplyPlan,
    updateInventorySupplyPlan,
    closeInventorySupplyPlan,
  };
}

export function mockInventoryQueries(
  overrides: { supplyPlans?: any[]; receiptEvidence?: any[] } = {},
): MockInventoryQueries {
  const getInventorySupplyPlan: MockInventoryQueries["getInventorySupplyPlan"] = async () =>
    ok({ supplyPlan: overrides.supplyPlans?.[0] ?? null });
  const listInventorySupplyPlans: MockInventoryQueries["listInventorySupplyPlans"] = async () =>
    ok({ items: overrides.supplyPlans ?? [], hasNextPage: false });
  const listPurchaseReceiptEvidenceForMatching: MockInventoryQueries["listPurchaseReceiptEvidenceForMatching"] =
    async (db, input) => {
      if (overrides.receiptEvidence) {
        return ok({ receiptEvidence: overrides.receiptEvidence });
      }
      const rows = await db.selectFrom("InventoryExecutionLine").selectAll().execute();
      const rowArray = Array.isArray(rows) ? rows : rows ? [rows] : [];
      return ok({
        receiptEvidence: rowArray
          .map((row) => {
            const record = row as Record<string, unknown>;
            const purchaseOrderLineId = String(
              record.purchaseOrderLineId ?? record.sourceLineId ?? input.purchaseOrderLineIds[0],
            );
            return {
              purchaseOrderLineId,
              inventoryExecutionLineId: String(record.inventoryExecutionLineId ?? record.id),
              inventoryExecutionId: String(record.inventoryExecutionId ?? "inventory-execution-1"),
              quantity: String(record.primaryQuantity ?? record.quantity),
              createdAt:
                record.createdAt instanceof Date
                  ? record.createdAt
                  : new Date("2024-01-01T00:00:00.000Z"),
            };
          })
          .filter((row) => input.purchaseOrderLineIds.includes(row.purchaseOrderLineId)),
      });
    };

  return {
    getInventorySupplyPlan,
    listInventorySupplyPlans,
    listPurchaseReceiptEvidenceForMatching,
  };
}
