import { computed, ref, watch } from 'vue'
import { useApi } from '~/composables/useApi'
import { extractCollection } from '~/shared/utils/apiHelpers'
import {
  componentOptionDescription,
  componentOptionLabel,
  describePieceRequirement as _describePieceRequirement,
  describeProductRequirement as _describeProductRequirement,
  pieceOptionDescription,
  pieceOptionLabel,
  productOptionDescription,
  productOptionLabel,
} from '~/shared/utils/structureAssignmentLabels'
import type {
  ComponentOption,
  PieceOption,
  ProductOption,
  StructureAssignmentNode,
  StructurePieceAssignment,
  StructureProductAssignment,
} from '~/shared/utils/structureAssignmentLabels'

export type {
  ComponentOption,
  PieceOption,
  ProductOption,
  StructureAssignmentNode,
  StructurePieceAssignment,
  StructureProductAssignment,
} from '~/shared/utils/structureAssignmentLabels'

export interface StructureAssignmentFetchDeps {
  assignment: StructureAssignmentNode
  pieces: PieceOption[] | null
  products: ProductOption[] | null
  components: ComponentOption[] | null
  isRoot: () => boolean
  pieceTypeLabelMap: Record<string, string>
  productTypeLabelMap: Record<string, string>
  componentTypeLabelMap: Record<string, string>
}

export function useStructureAssignmentFetch(deps: StructureAssignmentFetchDeps) {
  const { get } = useApi()

  const pieceOptionsByPath = ref<Record<string, PieceOption[]>>({})
  const productOptionsByPath = ref<Record<string, ProductOption[]>>({})
  const componentOptionsByPath = ref<Record<string, ComponentOption[]>>({})
  const pieceLoadingByPath = ref<Record<string, boolean>>({})
  const productLoadingByPath = ref<Record<string, boolean>>({})
  const componentLoadingByPath = ref<Record<string, boolean>>({})

  const setLoading = (target: Record<string, boolean>, key: string, value: boolean) => {
    target[key] = value
  }

  const typeIri = (id: string) => `/api/model_types/${id}`
  const primedPiecePaths = new Set<string>()
  const primedProductPaths = new Set<string>()
  const primedComponentPaths = new Set<string>()

  // --- Component options ---

  const componentOptions = computed(() => {
    if (deps.isRoot()) {
      return []
    }
    const cached = componentOptionsByPath.value[deps.assignment.path]
    if (cached) {
      return cached
    }
    const definition = deps.assignment.definition || {}
    const requiredTypeId =
      definition.typeComposantId || definition.modelId || null
    const requiredFamilyCode = definition.familyCode || null

    return (deps.components || []).filter((component) => {
      if (!component || typeof component !== 'object') {
        return false
      }
      if (requiredTypeId) {
        return component.typeComposantId === requiredTypeId
      }
      if (requiredFamilyCode) {
        return (
          component.typeComposant?.code === requiredFamilyCode
          || component.typeComposantId === requiredFamilyCode
        )
      }
      return true
    })
  })

  const fetchComponentOptions = async (term = '') => {
    if (deps.isRoot()) {
      return
    }
    const key = deps.assignment.path
    if (componentLoadingByPath.value[key]) {
      return
    }

    const definition = deps.assignment.definition || {}
    const requiredTypeId =
      definition.typeComposantId || definition.modelId || definition.typeComposant?.id || null

    const params = new URLSearchParams()
    params.set('itemsPerPage', '200')
    if (term.trim()) {
      params.set('search', term.trim())
    }
    if (requiredTypeId) {
      params.set('typeComposant', typeIri(requiredTypeId))
    }

    setLoading(componentLoadingByPath.value, key, true)
    try {
      const result = await get(`/composants?${params.toString()}`)
      if (result.success) {
        componentOptionsByPath.value[key] = extractCollection(result.data)
      }
    }
    finally {
      setLoading(componentLoadingByPath.value, key, false)
    }
  }

  // --- Piece options ---

  const getPieceOptions = (assignment: StructurePieceAssignment) => {
    const cached = pieceOptionsByPath.value[assignment.path]
    if (cached) {
      return cached
    }
    const definition = assignment.definition
    const requiredTypeId =
      definition.typePieceId
      || definition.typePiece?.id
      || definition.familyCode
      || null

    return (deps.pieces || []).filter((piece) => {
      if (!piece || typeof piece !== 'object') {
        return false
      }
      if (!requiredTypeId) {
        return true
      }
      if (definition.typePieceId || definition.typePiece?.id) {
        return (
          piece.typePieceId === requiredTypeId
          || piece.typePiece?.id === requiredTypeId
        )
      }
      if (definition.familyCode) {
        return (
          piece.typePiece?.code === requiredTypeId
          || piece.typePieceId === requiredTypeId
        )
      }
      return false
    })
  }

  const fetchPieceOptions = async (assignment: StructurePieceAssignment, term = '') => {
    const key = assignment.path
    if (pieceLoadingByPath.value[key]) {
      return
    }

    const definition = assignment.definition || {}
    const requiredTypeId =
      definition.typePieceId || definition.typePiece?.id || null

    const params = new URLSearchParams()
    params.set('itemsPerPage', '200')
    if (term.trim()) {
      params.set('search', term.trim())
    }
    if (requiredTypeId) {
      params.set('typePiece', typeIri(requiredTypeId))
    }

    setLoading(pieceLoadingByPath.value, key, true)
    try {
      const result = await get(`/pieces?${params.toString()}`)
      if (result.success) {
        pieceOptionsByPath.value[key] = extractCollection(result.data)
      }
    }
    finally {
      setLoading(pieceLoadingByPath.value, key, false)
    }
  }

  const describePieceRequirement = (assignment: StructurePieceAssignment) => {
    const options = getPieceOptions(assignment)
    return _describePieceRequirement(assignment, options, deps.pieceTypeLabelMap)
  }

  // --- Product options ---

  const getProductOptions = (assignment: StructureProductAssignment) => {
    const cached = productOptionsByPath.value[assignment.path]
    if (cached) {
      return cached
    }
    const definition = assignment.definition
    const requiredTypeId =
      definition.typeProductId
      || definition.typeProduct?.id
      || definition.familyCode
      || null

    return (deps.products || []).filter((product) => {
      if (!product || typeof product !== 'object') {
        return false
      }
      if (!requiredTypeId) {
        return true
      }
      if (definition.typeProductId || definition.typeProduct?.id) {
        return (
          product.typeProductId === requiredTypeId
          || product.typeProduct?.id === requiredTypeId
        )
      }
      if (definition.familyCode) {
        return (
          product.typeProduct?.code === requiredTypeId
          || product.typeProductId === requiredTypeId
        )
      }
      return false
    })
  }

  const fetchProductOptions = async (assignment: StructureProductAssignment, term = '') => {
    const key = assignment.path
    if (productLoadingByPath.value[key]) {
      return
    }

    const definition = assignment.definition || {}
    const requiredTypeId =
      definition.typeProductId || definition.typeProduct?.id || null

    const params = new URLSearchParams()
    params.set('itemsPerPage', '200')
    if (term.trim()) {
      params.set('search', term.trim())
    }
    if (requiredTypeId) {
      params.set('typeProduct', typeIri(requiredTypeId))
    }

    setLoading(productLoadingByPath.value, key, true)
    try {
      const result = await get(`/products?${params.toString()}`)
      if (result.success) {
        productOptionsByPath.value[key] = extractCollection(result.data)
      }
    }
    finally {
      setLoading(productLoadingByPath.value, key, false)
    }
  }

  const describeProductRequirement = (assignment: StructureProductAssignment) => {
    const options = getProductOptions(assignment)
    return _describeProductRequirement(assignment, options, deps.productTypeLabelMap)
  }

  // --- Watchers ---

  watch(
    componentOptions,
    (options) => {
      if (deps.isRoot()) {
        return
      }
      // Only clear if we have loaded options (cache or catalog); skip when options are empty
      // because the fetch may not have completed yet.
      if (!options.length) {
        return
      }
      const hasMatch = options.some(
        (component) => component.id === deps.assignment.selectedComponentId,
      )
      if (!hasMatch) {
        deps.assignment.selectedComponentId = ''
      }
    },
    { immediate: true },
  )

  watch(
    () => [deps.pieces, deps.assignment.pieces],
    () => {
      for (const pieceAssignment of deps.assignment.pieces) {
        const hasCachedOptions = !!pieceOptionsByPath.value[pieceAssignment.path]
        // Only clear selections when we have loaded options (cached or from catalog).
        // When no cache exists, a fetch is about to fire — clearing now would lose
        // user input before the real option list arrives.
        if (hasCachedOptions) {
          const options = getPieceOptions(pieceAssignment)
          if (
            pieceAssignment.selectedPieceId
            && !options.some((piece) => piece.id === pieceAssignment.selectedPieceId)
          ) {
            pieceAssignment.selectedPieceId = ''
          }
        }
        if (!primedPiecePaths.has(pieceAssignment.path) && !pieceOptionsByPath.value[pieceAssignment.path]) {
          primedPiecePaths.add(pieceAssignment.path)
          fetchPieceOptions(pieceAssignment).catch(() => {})
        }
      }
    },
    { deep: true, immediate: true },
  )

  watch(
    () => [deps.products, deps.assignment.products],
    () => {
      for (const productAssignment of deps.assignment.products) {
        const hasCachedOptions = !!productOptionsByPath.value[productAssignment.path]
        if (hasCachedOptions) {
          const options = getProductOptions(productAssignment)
          if (
            productAssignment.selectedProductId
            && !options.some((product) => product.id === productAssignment.selectedProductId)
          ) {
            productAssignment.selectedProductId = ''
          }
        }
        if (!primedProductPaths.has(productAssignment.path) && !productOptionsByPath.value[productAssignment.path]) {
          primedProductPaths.add(productAssignment.path)
          fetchProductOptions(productAssignment).catch(() => {})
        }
      }
    },
    { deep: true, immediate: true },
  )

  watch(
    () => deps.assignment.definition,
    () => {
      if (deps.isRoot()) {
        return
      }
      const key = deps.assignment.path
      if (!primedComponentPaths.has(key) && !componentOptionsByPath.value[key]) {
        primedComponentPaths.add(key)
        fetchComponentOptions().catch(() => {})
      }
    },
    { immediate: true },
  )

  return {
    pieceLoadingByPath,
    productLoadingByPath,
    componentLoadingByPath,
    componentOptions,
    componentOptionLabel,
    componentOptionDescription,
    fetchComponentOptions,
    getPieceOptions,
    pieceOptionLabel,
    pieceOptionDescription,
    fetchPieceOptions,
    describePieceRequirement,
    getProductOptions,
    productOptionLabel,
    productOptionDescription,
    fetchProductOptions,
    describeProductRequirement,
  }
}
