import type { Transaction } from "../generated/kysely-tailordb";
import {
  WarehouseNotFoundError,
  WarehouseInactiveError,
  InvalidLocationTypeError,
  ParentZoneNotFoundError,
  ParentNotAZoneError,
  ParentInDifferentWarehouseError,
  DuplicateLocationNameError,
} from "../lib/errors.generated";
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface CreateStorageLocationInput {
  name: string;
  code: string;
  warehouseId: string;
  locationType: string;
  parentId?: string | null;
  storageCondition?: string | null;
  capacity?: number | null;
}

/**
 * Function: createStorageLocation
 *
 * Creates a new storage location (zone or bin) within a warehouse.
 * Zones are top-level divisions; bins are subdivisions within a zone.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreateStorageLocationInput & CF,
  _ctx: CommandContext,
) {
  const {
    name,
    code,
    warehouseId,
    locationType,
    parentId,
    storageCondition,
    capacity,
    ...customFields
  } = input;

  // 1. Validate warehouse exists
  const warehouse = await db
    .selectFrom("Warehouse")
    .selectAll()
    .where("id", "=", warehouseId)
    .forUpdate()
    .executeTakeFirst();

  if (!warehouse) {
    return err(new WarehouseNotFoundError(warehouseId));
  }

  // 2. Validate warehouse is ACTIVE
  if (warehouse.status !== "ACTIVE") {
    return err(new WarehouseInactiveError(warehouseId));
  }

  // 3. Validate locationType
  if (locationType !== "zone" && locationType !== "bin") {
    return err(new InvalidLocationTypeError(locationType));
  }

  // 4. Validate hierarchy constraints
  if (locationType === "bin") {
    if (!parentId) {
      return err(new ParentZoneNotFoundError(parentId ?? "null"));
    }

    const parent = await db
      .selectFrom("StorageLocation")
      .selectAll()
      .where("id", "=", parentId)
      .forUpdate()
      .executeTakeFirst();

    if (!parent) {
      return err(new ParentZoneNotFoundError(parentId));
    }

    if (parent.locationType !== "zone") {
      return err(new ParentNotAZoneError(parentId));
    }

    if (parent.warehouseId !== warehouseId) {
      return err(new ParentInDifferentWarehouseError(parentId));
    }
  }

  // 5. Validate name and code uniqueness within warehouse
  const duplicateName = await db
    .selectFrom("StorageLocation")
    .selectAll()
    .where("warehouseId", "=", warehouseId)
    .where((eb) => eb.or([eb("name", "=", name), eb("code", "=", code)]))
    .forUpdate()
    .executeTakeFirst();

  if (duplicateName) {
    return err(new DuplicateLocationNameError(name));
  }

  // 6. Create storage location
  const storageLocation = await db
    .insertInto("StorageLocation")
    .values({
      ...(customFields as Record<string, unknown>),
      name,
      code,
      warehouseId,
      locationType,
      parentId: locationType === "zone" ? null : (parentId ?? null),
      storageCondition: storageCondition ?? null,
      capacity: capacity ?? null,
      status: "ACTIVE",
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  return ok({ storageLocation: storageLocation! });
}
