/**
 * Builds a component/piece hierarchy tree from flat machine link arrays.
 *
 * Extracted from pages/machine/[id].vue to keep the page orchestrator lean.
 */

import { resolveIdentifier, getProductDisplay } from '~/shared/utils/productDisplayUtils'
import { resolveConstructeurs, uniqueConstructeurIds, type ConstructeurSummary } from '~/shared/constructeurUtils'

type AnyRecord = Record<string, unknown>

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const collectConstructeurs = (
  allConstructeurs: AnyRecord[],
  ...sources: unknown[]
): AnyRecord[] => {
  const ids = uniqueConstructeurIds(...sources)
  if (!ids.length) return []

  const pools = sources
    .flatMap((source) => {
      if (Array.isArray(source)) return [source]
      if (source && typeof source === 'object' && (source as AnyRecord).id) return [[source]]
      return []
    })
    .filter(Boolean) as AnyRecord[][]

  // ConstructeurSummary and AnyRecord are structurally compatible at runtime
  const allPools = [...pools, allConstructeurs] as unknown as Array<ConstructeurSummary[]>
  return resolveConstructeurs(ids, ...allPools) as unknown as AnyRecord[]
}

// ---------------------------------------------------------------------------
// Link array resolution
// ---------------------------------------------------------------------------

export const resolveLinkArray = (source: unknown, keys: string[]): unknown[] | null => {
  if (!source || typeof source !== 'object') return null
  for (const key of keys) {
    const value = (source as AnyRecord)[key]
    if (Array.isArray(value)) return value
  }
  return null
}

// ---------------------------------------------------------------------------
// Merge trees (for incremental updates)
// ---------------------------------------------------------------------------

export function mergePieceLists(existing: AnyRecord[] = [], updates: AnyRecord[] = []): AnyRecord[] {
  if (!existing.length) {
    return updates.map((piece) => ({ ...piece, constructeurs: piece.constructeurs || [] }))
  }
  if (!updates.length) {
    return existing.map((piece) => ({ ...piece, constructeurs: piece.constructeurs || [] }))
  }

  const updateMap = new Map<unknown, AnyRecord>(
    updates.map((piece) => [piece.id, { ...piece, constructeurs: piece.constructeurs || [] }]),
  )
  const merged = existing.map((piece) => {
    const update = updateMap.get(piece.id)
    if (!update) return piece
    return { ...piece, ...update, customFields: update.customFields ?? piece.customFields }
  })

  updates.forEach((update) => {
    if (!existing.some((piece) => piece.id === update.id)) merged.push(update)
  })

  return merged
}

export function mergeComponentTrees(existing: AnyRecord[] = [], updates: AnyRecord[] = []): AnyRecord[] {
  if (!existing.length) {
    return updates.map((component) => ({
      ...component,
      constructeurs: component.constructeurs || [],
      pieces: ((component.pieces || []) as AnyRecord[]).map((piece) => ({
        ...piece,
        constructeurs: piece.constructeurs || [],
      })),
      subComponents: mergeComponentTrees([], (component.subComponents || []) as AnyRecord[]),
    }))
  }
  if (!updates.length) return existing

  const updateMap = new Map<unknown, AnyRecord>(
    updates.map((component) => [
      component.id,
      {
        ...component,
        constructeurs: component.constructeurs || [],
        pieces: ((component.pieces || []) as AnyRecord[]).map((piece) => ({
          ...piece,
          constructeurs: piece.constructeurs || [],
        })),
        subComponents: mergeComponentTrees([], (component.subComponents || []) as AnyRecord[]),
      },
    ]),
  )
  const merged: AnyRecord[] = existing.map((component) => {
    const update = updateMap.get(component.id)
    if (!update) {
      return {
        ...component,
        constructeurs: component.constructeurs || [],
        pieces: mergePieceLists((component.pieces || []) as AnyRecord[], []),
        subComponents: mergeComponentTrees((component.subComponents || []) as AnyRecord[], []),
      }
    }
    return {
      ...component,
      ...update,
      customFields: update.customFields ?? component.customFields,
      pieces: mergePieceLists((component.pieces || []) as AnyRecord[], (update.pieces || []) as AnyRecord[]),
      subComponents: mergeComponentTrees(
        (component.subComponents || []) as AnyRecord[],
        (update.subComponents || []) as AnyRecord[],
      ),
    }
  })

  updates.forEach((update) => {
    if (!existing.some((component) => component.id === update.id)) merged.push(update)
  })

  return merged
}

// ---------------------------------------------------------------------------
// Build hierarchy from links
// ---------------------------------------------------------------------------

export const buildMachineHierarchyFromLinks = (
  componentLinks: AnyRecord[] = [],
  pieceLinks: AnyRecord[] = [],
  findProductById: (id: string) => AnyRecord | null,
  allConstructeurs: AnyRecord[] = [],
): { components: AnyRecord[]; machinePieces: AnyRecord[] } => {
  const normalizeComponentLinkId = (link: AnyRecord) =>
    resolveIdentifier(link?.id, link?.linkId, link?.machineComponentLinkId)

  const normalizePieceLinkId = (link: AnyRecord) =>
    resolveIdentifier(link?.id, link?.linkId, link?.machinePieceLinkId)

  const createPieceNode = (link: AnyRecord, parentComponentName: string | null = null): AnyRecord | null => {
    if (!link || typeof link !== 'object') return null

    // Category-only link (no entity yet)
    if (link.pendingEntity || (!link.piece && !link.pieceId)) {
      const machinePieceLinkId = normalizePieceLinkId(link)
      const mt = (link.modelType || null) as AnyRecord | null
      return {
        id: machinePieceLinkId || `pending-${link.id}`,
        linkId: machinePieceLinkId,
        name: mt?.name || 'Catégorie sans item',
        reference: null,
        prix: null,
        pendingEntity: true,
        modelTypeId: link.modelTypeId || mt?.id || null,
        modelType: mt,
        pieceId: null,
        constructeurs: [],
        documents: [],
        customFields: [],
        parentComponentLinkId: link.parentComponentLinkId || link.parentLinkId || null,
        parentComponentName,
        machinePieceLinkId,
        quantity: 1,
      }
    }

    const appliedPiece = (link.piece && typeof link.piece === 'object' ? link.piece : {}) as AnyRecord
    const originalPiece = (link.originalPiece && typeof link.originalPiece === 'object' ? link.originalPiece : null) as AnyRecord | null

    const machinePieceLinkId = normalizePieceLinkId(link)
    const pieceId = resolveIdentifier(appliedPiece.id, appliedPiece.pieceId, link.pieceId)

    const overrides = (link.overrides || null) as AnyRecord | null

    const basePiece: AnyRecord = {
      ...appliedPiece,
      id: appliedPiece.id || pieceId || machinePieceLinkId || `piece-${machinePieceLinkId}`,
      pieceId,
      name: overrides?.name || appliedPiece.name || (appliedPiece.definition as AnyRecord)?.name || (appliedPiece.definition as AnyRecord)?.role || originalPiece?.name || 'Pièce',
      reference: overrides?.reference || appliedPiece.reference || (appliedPiece.definition as AnyRecord)?.reference || originalPiece?.reference || null,
      prix: overrides?.prix ?? appliedPiece.prix ?? originalPiece?.prix ?? null,
      constructeur: appliedPiece.constructeur || originalPiece?.constructeur || null,
      constructeurId: appliedPiece.constructeurId || (appliedPiece.constructeur as AnyRecord)?.id || originalPiece?.constructeurId || null,
      documents: Array.isArray(appliedPiece.documents) ? appliedPiece.documents : Array.isArray(originalPiece?.documents) ? originalPiece!.documents : [],
      typePiece: appliedPiece.typePiece || null,
      typePieceId: appliedPiece.typePieceId || (appliedPiece.typePiece as AnyRecord)?.id || null,
      overrides,
      originalPiece,
      machinePieceLink: link,
      machinePieceLinkId,
      linkId: machinePieceLinkId,
      parentComponentLinkId: resolveIdentifier(link.parentComponentLinkId, link.parentLinkId, link.parentMachineComponentLinkId, appliedPiece.parentComponentLinkId),
      parentComponentId: resolveIdentifier(appliedPiece.parentComponentId, link.parentComponentId),
      parentComponentName,
      parentLinkId: resolveIdentifier(link.parentLinkId, link.parentMachinePieceLinkId, appliedPiece.parentLinkId),
      parentPieceLinkId: resolveIdentifier(link.parentPieceLinkId, appliedPiece.parentPieceLinkId),
      parentPieceId: resolveIdentifier(appliedPiece.parentPieceId, link.parentPieceId),
      quantity: typeof link.quantity === 'number' ? link.quantity : 1,
      definition: appliedPiece.definition || originalPiece?.definition || {},
      customFields: appliedPiece.customFields || [],
      contextCustomFields: link.contextCustomFields || [],
      contextCustomFieldValues: link.contextCustomFieldValues || [],
    }

    const resolvedProductId = resolveIdentifier(appliedPiece.productId, (appliedPiece.product as AnyRecord)?.id, link.productId, (link.product as AnyRecord)?.id, originalPiece?.productId, (originalPiece?.product as AnyRecord)?.id)
    const resolvedProduct = (appliedPiece.product || link.product || originalPiece?.product || (resolvedProductId ? findProductById(resolvedProductId) : null) || null) as AnyRecord | null

    const constructeurs = collectConstructeurs(allConstructeurs, appliedPiece.constructeurs, appliedPiece.constructeur, appliedPiece.constructeurIds, appliedPiece.constructeurId, originalPiece?.constructeurs, originalPiece?.constructeur, originalPiece?.constructeurIds, originalPiece?.constructeurId)

    return {
      ...basePiece,
      constructeurs,
      constructeur: constructeurs[0] || basePiece.constructeur || null,
      constructeurId: (constructeurs[0] as AnyRecord)?.id || basePiece.constructeurId || null,
      productId: resolvedProductId || appliedPiece.productId || null,
      product: resolvedProduct || appliedPiece.product || null,
      __productDisplay: getProductDisplay({ product: resolvedProduct || appliedPiece.product || null, productId: resolvedProductId || appliedPiece.productId || null } as AnyRecord, findProductById),
    }
  }

  const createComponentNode = (link: AnyRecord): AnyRecord | null => {
    if (!link || typeof link !== 'object') return null

    // Category-only link (no entity yet)
    if (link.pendingEntity || (!link.composant && !link.composantId)) {
      const machineComponentLinkId = normalizeComponentLinkId(link)
      const mt = (link.modelType || null) as AnyRecord | null
      return {
        id: machineComponentLinkId || `pending-${link.id}`,
        linkId: machineComponentLinkId,
        name: mt?.name || 'Catégorie sans item',
        reference: null,
        prix: null,
        pendingEntity: true,
        modelTypeId: link.modelTypeId || mt?.id || null,
        modelType: mt,
        composantId: null,
        composant: null,
        constructeurs: [],
        documents: [],
        customFields: [],
        customFieldValues: [],
        subComponents: [],
        pieces: [],
        overrides: null,
        parentComponentLinkId: link.parentComponentLinkId || link.parentLinkId || null,
        machineComponentLinkId,
        childLinks: [],
        pieceLinks: [],
      }
    }

    const appliedComponent = (link.composant && typeof link.composant === 'object' ? link.composant : {}) as AnyRecord
    const originalComponent = (link.originalComposant && typeof link.originalComposant === 'object' ? link.originalComposant : null) as AnyRecord | null

    const machineComponentLinkId = normalizeComponentLinkId(link)
    const composantId = resolveIdentifier(appliedComponent.id, appliedComponent.composantId, link.composantId)

    const compOverrides = (link.overrides || null) as AnyRecord | null

    const componentName = (compOverrides?.name || appliedComponent.name || (appliedComponent.definition as AnyRecord)?.alias || (appliedComponent.definition as AnyRecord)?.name || originalComponent?.name || 'Composant') as string

    const linkedPieces = Array.isArray(link.pieceLinks)
      ? (link.pieceLinks as AnyRecord[]).map((pl) => createPieceNode(pl, componentName)).filter(Boolean) as AnyRecord[]
      : []

    // If no linked pieces exist, build read-only entries from the composant's structure
    const structurePieceDefs = (!linkedPieces.length && appliedComponent.structure && typeof appliedComponent.structure === 'object')
      ? (Array.isArray((appliedComponent.structure as AnyRecord).pieces) ? (appliedComponent.structure as AnyRecord).pieces as AnyRecord[] : [])
      : []
    const structurePieces = structurePieceDefs.map((def, index) => {
      const definition = (def.definition && typeof def.definition === 'object' ? def.definition : def) as AnyRecord
      const resolved = (def.resolvedPiece && typeof def.resolvedPiece === 'object' ? def.resolvedPiece : null) as AnyRecord | null
      const quantity = typeof definition.quantity === 'number' ? definition.quantity : (typeof def.quantity === 'number' ? def.quantity : 1)
      const isEmpty = !resolved
      const typePieceName = (resolved?.typePiece as AnyRecord)?.name || (definition.typePiece as AnyRecord)?.name || (def.typePiece as AnyRecord)?.name || null
      return {
        ...(resolved || {}),
        id: resolved?.id || `structure-piece-${composantId}-${index}`,
        pieceId: resolved?.id || null,
        name: resolved?.name || definition.role || definition.name || def.role || def.name || (typePieceName ? `${typePieceName}` : `Pièce ${index + 1}`),
        reference: resolved?.reference || definition.reference || def.reference || null,
        prix: resolved?.prix ?? null,
        constructeurs: resolved?.constructeurs || [],
        documents: [],
        quantity,
        slotId: def.slotId || definition.slotId || null,
        typePieceId: resolved?.typePieceId || definition.typePieceId || def.typePieceId || null,
        typePiece: resolved?.typePiece || null,
        parentComponentLinkId: machineComponentLinkId,
        parentComponentName: componentName,
        _structurePiece: true,
        _emptySlot: isEmpty,
      }
    }) as AnyRecord[]

    const pieces = linkedPieces.length ? linkedPieces : structurePieces

    const subComponents = Array.isArray(link.childLinks)
      ? (link.childLinks as AnyRecord[]).map(createComponentNode).filter(Boolean) as AnyRecord[]
      : []

    const resolvedProductId = resolveIdentifier(appliedComponent.productId, (appliedComponent.product as AnyRecord)?.id, link.productId, (link.product as AnyRecord)?.id, originalComponent?.productId, (originalComponent?.product as AnyRecord)?.id)
    const resolvedProduct = (appliedComponent.product || link.product || originalComponent?.product || (resolvedProductId ? findProductById(resolvedProductId) : null) || null) as AnyRecord | null

    const baseComponent: AnyRecord = {
      ...appliedComponent,
      id: appliedComponent.id || composantId || machineComponentLinkId || `component-${machineComponentLinkId}`,
      composantId,
      name: componentName,
      reference: compOverrides?.reference || appliedComponent.reference || (appliedComponent.definition as AnyRecord)?.reference || originalComponent?.reference || null,
      prix: compOverrides?.prix ?? appliedComponent.prix ?? originalComponent?.prix ?? null,
      constructeur: appliedComponent.constructeur || originalComponent?.constructeur || null,
      constructeurId: appliedComponent.constructeurId || (appliedComponent.constructeur as AnyRecord)?.id || originalComponent?.constructeurId || null,
      documents: Array.isArray(appliedComponent.documents) ? appliedComponent.documents : Array.isArray(originalComponent?.documents) ? originalComponent!.documents : [],
      typeComposant: appliedComponent.typeComposant || null,
      typeComposantId: appliedComponent.typeComposantId || (appliedComponent.typeComposant as AnyRecord)?.id || null,
      overrides: compOverrides,
      machineComponentLinkOverrides: compOverrides,
      definitionOverrides: compOverrides,
      originalComposant: originalComponent,
      machineComponentLink: link,
      machineComponentLinkId,
      linkId: machineComponentLinkId,
      componentLinkId: machineComponentLinkId,
      parentComponentLinkId: resolveIdentifier(link.parentComponentLinkId, link.parentLinkId, link.parentMachineComponentLinkId, appliedComponent.parentComponentLinkId),
      parentComposantId: resolveIdentifier(appliedComponent.parentComposantId, link.parentComponentId),
      definition: appliedComponent.definition || originalComponent?.definition || {},
      customFields: appliedComponent.customFields || [],
      contextCustomFields: link.contextCustomFields || [],
      contextCustomFieldValues: link.contextCustomFieldValues || [],
      pieces,
      subComponents,
      subcomponents: subComponents,
      sousComposants: subComponents,
    }

    const constructeurs = collectConstructeurs(allConstructeurs, appliedComponent.constructeurs, appliedComponent.constructeur, appliedComponent.constructeurIds, appliedComponent.constructeurId, originalComponent?.constructeurs, originalComponent?.constructeur, originalComponent?.constructeurIds, originalComponent?.constructeurId)

    return {
      ...baseComponent,
      constructeurs,
      constructeur: constructeurs[0] || baseComponent.constructeur || null,
      constructeurId: (constructeurs[0] as AnyRecord)?.id || baseComponent.constructeurId || null,
      productId: resolvedProductId || appliedComponent.productId || null,
      product: resolvedProduct || appliedComponent.product || null,
      __productDisplay: getProductDisplay({ product: resolvedProduct || appliedComponent.product || null, productId: resolvedProductId || appliedComponent.productId || null } as AnyRecord, findProductById),
    }
  }

  const rootComponents = (Array.isArray(componentLinks) ? componentLinks : [])
    .filter((link) => !resolveIdentifier(link?.parentComponentLinkId, link?.parentLinkId, link?.parentMachineComponentLinkId))
    .map(createComponentNode)
    .filter(Boolean) as AnyRecord[]

  const machinePieces = (Array.isArray(pieceLinks) ? pieceLinks : [])
    .filter((link) => !resolveIdentifier(link?.parentComponentLinkId, link?.parentLinkId, link?.parentMachineComponentLinkId))
    .map((link) => createPieceNode(link, null))
    .filter(Boolean) as AnyRecord[]

  return { components: rootComponents, machinePieces }
}
