import { ok, err, type ReadonlyDB } from "@tailor-platform/erp-kit/core";
import type { DB } from "../generated/kysely-tailordb";
import { CompanyScopeRequiredError, ItemReferenceRequiredError } from "../lib/errors.generated";

export interface DetectBillOfMaterialCircularReferenceInput {
  companyId: string;
  parentItemId: string;
  childItemId: string;
}

export async function run(db: ReadonlyDB<DB>, input: DetectBillOfMaterialCircularReferenceInput) {
  if (!input.companyId) {
    return err(new CompanyScopeRequiredError("companyId"));
  }

  if (!input.parentItemId || !input.childItemId) {
    return err(new ItemReferenceRequiredError("parentItemId and childItemId"));
  }

  // If child === parent, immediate circular reference
  if (input.childItemId === input.parentItemId) {
    return ok({
      hasCircularReference: true,
      path: [input.parentItemId, input.childItemId],
    });
  }

  // BFS from childItemId through active BOMs to see if any path reaches parentItemId
  type BfsNode = { itemId: string; path: string[] };
  const visited = new Set<string>();
  const queue: BfsNode[] = [];
  let current: BfsNode | undefined = {
    itemId: input.childItemId,
    path: [input.parentItemId, input.childItemId],
  };
  do {
    if (visited.has(current.itemId)) {
      continue;
    }
    visited.add(current.itemId);

    // Find active BOMs where this item is the parent
    const activeBoms = await db
      .selectFrom("BillOfMaterial")
      .select("id")
      .where("parentItemId", "=", current.itemId)
      .where("companyId", "=", input.companyId)
      .where("status", "=", "ACTIVE")
      .execute();

    if (activeBoms.length === 0) {
      continue;
    }

    const bomIds = activeBoms.map((b) => b.id);

    // Find subassembly lines for these BOMs
    const subassemblyLines = await db
      .selectFrom("BillOfMaterialLine")
      .select("itemId")
      .where("billOfMaterialId", "in", bomIds)
      .where("isSubassembly", "=", true)
      .execute();

    for (const line of subassemblyLines) {
      const newPath = [...current.path, line.itemId];

      if (line.itemId === input.parentItemId) {
        return ok({ hasCircularReference: true, path: newPath });
      }

      if (!visited.has(line.itemId)) {
        queue.push({ itemId: line.itemId, path: newPath });
      }
    }
  } while ((current = queue.shift()) != null);

  return ok({ hasCircularReference: false, path: null });
}
