import {
  type CommonReservedFields,
  type NoReservedFields,
  defaultGqlPermission,
  defaultPermission,
} from "@tailor-platform/erp-kit/core";
import { db, type TailorAnyDBField, type TailorAnyDBType } from "@tailor-platform/sdk";
import { inventoryExecution } from "./inventoryExecution";
import { lot } from "./lot";
import { serialNumber } from "./serialNumber";
import { STOCK_TYPES } from "./stockLevel";
import { storageLocation } from "./storageLocation";

export const INVENTORY_EXECUTION_LINE_DIRECTIONS = ["IN", "OUT"] as const;

const builtins = (params: { itemType?: TailorAnyDBType; unitType?: TailorAnyDBType }) => {
  const itemId = db.uuid().description("Foreign key to Item");
  const unitId = db.uuid().description("Business/input Unit for quantity");
  const primaryUnitId = db
    .uuid()
    .description("Snapshot of the item's primary Unit at execution creation time");
  return {
    inventoryExecutionId: db
      .uuid()
      .relation({
        type: "n-1",
        toward: { type: inventoryExecution },
        backward: "lines",
      })
      .description("Foreign key to InventoryExecution header"),
    direction: db
      .enum(INVENTORY_EXECUTION_LINE_DIRECTIONS)
      .description("IN or OUT stock direction for this execution line"),
    itemId: params.itemType
      ? itemId.relation({
          type: "n-1",
          toward: { type: params.itemType },
          backward: "inventoryExecutionLines",
        })
      : itemId,
    quantity: db.decimal().description("Business/input quantity in unitId"),
    unitId: params.unitType
      ? unitId.relation({
          type: "n-1",
          toward: { type: params.unitType },
          backward: "inventoryExecutionLines",
        })
      : unitId,
    primaryQuantity: db
      .decimal()
      .description("Quantity converted to the item's primary unit at execution creation time"),
    primaryUnitId: params.unitType
      ? primaryUnitId.relation({
          type: "n-1",
          toward: { type: params.unitType, as: "primaryUnit" },
          backward: "primaryInventoryExecutionLines",
        })
      : primaryUnitId,
    unitConversionRate: db
      .decimal()
      .description("Multiplier used to convert quantity into primaryQuantity"),
    unitCost: db
      .decimal({ optional: true })
      .description("Optional unit cost snapshot used by goods receipt valuation"),
    storageLocationId: db
      .uuid()
      .relation({
        type: "n-1",
        toward: { type: storageLocation },
        backward: "inventoryExecutionLines",
      })
      .description("Storage location affected by this execution line"),
    stockType: db
      .enum([...STOCK_TYPES] as [string, ...string[]])
      .description("Stock type affected by this execution line"),
    sourceLineId: db
      .uuid({ optional: true })
      .description("Optional source document line ID. Generic reference, not a typed foreign key."),
    lotId: db
      .uuid({ optional: true })
      .relation({
        type: "n-1",
        toward: { type: lot },
        backward: "inventoryExecutionLines",
      })
      .description("Optional Lot for lot-tracked items"),
    serialNumberId: db
      .uuid({ optional: true })
      .relation({
        type: "n-1",
        toward: { type: serialNumber },
        backward: "inventoryExecutionLines",
      })
      .description("Optional SerialNumber for serial-tracked items"),
  };
};

type ReservedFields = keyof ReturnType<typeof builtins> | CommonReservedFields;

export interface CreateInventoryExecutionLineTypeParams<
  F extends Record<string, TailorAnyDBField>,
> {
  fields?: NoReservedFields<F, ReservedFields>;
  itemType?: TailorAnyDBType;
  unitType?: TailorAnyDBType;
}

export function createInventoryExecutionLineType<const F extends Record<string, TailorAnyDBField>>(
  params: CreateInventoryExecutionLineTypeParams<F>,
) {
  return db
    .type("InventoryExecutionLine", {
      ...builtins(params),
      ...((params.fields ?? {}) as F),
      ...db.fields.timestamps(),
    })
    .indexes({
      fields: ["sourceLineId", "storageLocationId", "stockType", "inventoryExecutionId"],
      unique: false,
      name: "inventory_execution_line_source_stock_idx",
    })
    .permission(defaultPermission)
    .gqlPermission(defaultGqlPermission);
}

export const inventoryExecutionLine = createInventoryExecutionLineType({});
