<template>
  <main class="mx-auto flex w-full max-w-5xl flex-col gap-8 px-4 py-8 sm:px-6 lg:px-8">
    <header class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
      <div class="space-y-1">
        <h1 class="text-3xl font-semibold text-base-content">Nouvel composant</h1>
        <p class="text-sm text-base-content/70">
          Sélectionnez la catégorie cible puis complétez les informations du composant.
        </p>
      </div>
      <button type="button" class="btn btn-ghost btn-sm md:btn-md self-start" @click="$router.back()">
        Retour au catalogue
      </button>
    </header>

    <section class="card border border-base-200 bg-base-100 shadow-sm">
      <div class="card-body space-y-6">
        <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
          <div class="form-control">
            <label class="label">
              <span class="label-text">Catégorie de composant</span>
            </label>
            <SearchSelect
              v-model="selectedTypeId"
              :options="componentTypeList"
              :loading="loadingTypes"
              size="sm"
              placeholder="Rechercher une catégorie..."
              empty-text="Aucune catégorie disponible"
              :option-label="typeOptionLabel"
              :option-description="typeOptionDescription"
              :disabled="!canEdit || loadingTypes || submitting"
            />
            <p v-if="loadingTypes" class="text-xs text-gray-500 mt-1">
              Chargement des catégories…
            </p>
          </div>
        </div>

        <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
          <div class="form-control">
            <label class="label">
              <span class="label-text">Nom du composant</span>
            </label>
            <input
              v-model="creationForm.name"
              type="text"
              class="input input-bordered input-sm md:input-md"
              :disabled="!canEdit || submitting || !selectedType"
              placeholder="Nom affiché dans le catalogue"
              required
            >
          </div>
        </div>

        <div class="form-control">
          <label class="label">
            <span class="label-text">Description</span>
          </label>
          <textarea
            v-model="creationForm.description"
            class="textarea textarea-bordered textarea-sm md:textarea-md"
            :disabled="!canEdit || submitting || !selectedType"
            placeholder="Description du composant (optionnel)"
            rows="3"
          />
        </div>

        <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
          <div class="form-control">
            <label class="label">
              <span class="label-text">Référence</span>
            </label>
            <input
              v-model="creationForm.reference"
              type="text"
              class="input input-bordered input-sm md:input-md"
              :disabled="!canEdit || submitting || !selectedType"
              placeholder="Référence interne ou fournisseur"
            >
          </div>

          <div class="form-control">
            <label class="label">
              <span class="label-text">Fournisseur</span>
            </label>
            <ConstructeurSelect
              v-model="creationForm.constructeurIds"
              class="w-full"
              :disabled="!canEdit || submitting || !selectedType"
              placeholder="Rechercher un ou plusieurs fournisseurs..."
            />
          </div>
        </div>

        <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
          <div class="form-control">
            <label class="label">
              <span class="label-text">Prix indicatif (€)</span>
            </label>
            <input
              v-model="creationForm.prix"
              type="number"
              step="0.01"
              min="0"
              class="input input-bordered input-sm md:input-md"
              :disabled="!canEdit || submitting || !selectedType"
              placeholder="Valeur indicatrice"
            >
          </div>
        </div>

        <StructureSkeletonPreview
          v-if="selectedType"
          :structure="selectedTypeStructure"
          :description="selectedType.description || 'Ce squelette définit la structure et les contraintes du composant.'"
          :preview-badge="formatStructurePreview(selectedTypeStructure)"
          variant="component"
          :resolve-piece-label="resolvePieceLabel"
          :resolve-product-label="resolveProductLabel"
          :resolve-subcomponent-label="resolveSubcomponentLabel"
        />

        <div
          v-if="structureHasRequirements"
          class="space-y-4 rounded-lg border border-primary/30 bg-primary/5 p-4"
        >
          <div class="flex items-start justify-between gap-4">
            <div>
              <h2 class="font-semibold text-base-content">
                Sélection des éléments du squelette
              </h2>
              <p class="text-xs text-base-content/70">
                Affectez les pièces et sous-composants concrets correspondant à la catégorie choisie.
              </p>
            </div>
            <span
              class="badge"
              :class="structureSelectionsComplete ? 'badge-success' : structureDataLoading ? 'badge-info' : 'badge-warning'"
            >
              {{ structureSelectionsComplete ? 'Complet' : structureDataLoading ? 'Chargement…' : 'Incomplet' }}
            </span>
          </div>

          <div
            v-if="structureDataLoading"
            class="flex items-center gap-3 rounded-md border border-base-200 bg-base-100 p-3 text-sm text-base-content/70"
          >
            <span class="loading loading-spinner loading-sm" aria-hidden="true"></span>
            Chargement du catalogue de pièces, produits et composants…
          </div>
          <ComponentStructureAssignmentNode
            v-else-if="structureAssignments"
            :assignment="structureAssignments"
            :pieces="availablePieces"
            :products="availableProducts"
            :components="availableComponents"
            :pieces-loading="piecesLoading"
            :products-loading="productsLoading"
            :components-loading="componentsLoading"
            :piece-type-label-map="pieceTypeLabelMap"
            :product-type-label-map="productTypeLabelMap"
            :component-type-label-map="componentTypeLabelMap"
          />
          <p v-else class="text-xs text-error">
            Impossible de générer les emplacements définis par le squelette.
          </p>
        </div>

        <div v-if="customFieldInputs.length" class="space-y-4 rounded-lg border border-base-200 bg-base-200/40 p-4">
          <header class="space-y-1">
            <h2 class="font-semibold text-base-content">Champs personnalisés</h2>
            <p class="text-xs text-base-content/70">
              Renseignez les valeurs propres à ce composant selon le squelette choisi.
            </p>
          </header>
          <CustomFieldInputGrid :fields="customFieldInputs" :disabled="!canEdit || submitting" />
        </div>

        <div class="space-y-4 rounded-lg border border-base-200 bg-base-200/40 p-4">
          <header class="flex flex-col gap-1 md:flex-row md:items-center md:justify-between">
            <div>
              <h2 class="font-semibold text-base-content">Documents</h2>
              <p class="text-xs text-base-content/70">
                Ajoutez des documents (PDF, images, textes…) liés à ce composant.
              </p>
            </div>
            <span v-if="selectedDocuments.length" class="badge badge-outline">
              {{ selectedDocuments.length }} document{{ selectedDocuments.length > 1 ? 's' : '' }} prêt{{ selectedDocuments.length > 1 ? 's' : '' }} à être ajouté{{ selectedDocuments.length > 1 ? 's' : '' }}
            </span>
          </header>
          <div :class="{ 'pointer-events-none opacity-60': !canEdit || submitting }">
            <DocumentUpload
              v-model="selectedDocuments"
              title="Déposer vos fichiers"
              subtitle="Formats acceptés : PDF, images, documents…"
            />
          </div>
          <p v-if="uploadingDocuments" class="text-xs text-base-content/70">
            Téléversement des documents en cours…
          </p>
        </div>

        <div class="flex flex-col gap-3 md:flex-row md:justify-end">
          <NuxtLink to="/component-catalog" class="btn btn-ghost" :class="{ 'btn-disabled': submitting }">
            Annuler
          </NuxtLink>
          <button type="button" class="btn btn-primary" :disabled="!canSubmit" @click="submitCreation">
            <span v-if="submitting" class="loading loading-spinner loading-sm mr-2"></span>
            Créer le composant
          </button>
        </div>
      </div>
    </section>
  </main>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from '#imports'
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
import DocumentUpload from '~/components/DocumentUpload.vue'
import ComponentStructureAssignmentNode, {
  type StructureAssignmentNode,
} from '~/components/ComponentStructureAssignmentNode.vue'
import SearchSelect from '~/components/common/SearchSelect.vue'
import { useComponentTypes } from '~/composables/useComponentTypes'
import { useComposants } from '~/composables/useComposants'
import { usePieces } from '~/composables/usePieces'
import { usePieceTypes } from '~/composables/usePieceTypes'
import { useProducts } from '~/composables/useProducts'
import { useProductTypes } from '~/composables/useProductTypes'
import { useApi } from '~/composables/useApi'
import { useToast } from '~/composables/useToast'
import { humanizeError } from '~/shared/utils/errorMessages'
import { useCustomFields } from '~/composables/useCustomFields'
import { useDocuments } from '~/composables/useDocuments'
import { formatStructurePreview, normalizeStructureForEditor } from '~/shared/modelUtils'
import {
  type CustomFieldInput,
  normalizeCustomFieldInputs,
  requiredCustomFieldsFilled as _requiredCustomFieldsFilled,
  saveCustomFieldValues as _saveCustomFieldValues,
} from '~/shared/utils/customFieldFormUtils'
import { uniqueConstructeurIds } from '~/shared/constructeurUtils'
import {
  getStructurePieces,
  resolvePieceLabel as _resolvePieceLabel,
  resolveProductLabel as _resolveProductLabel,
  resolveSubcomponentLabel,
  fetchModelTypeNames,
  buildTypeLabelMap,
} from '~/shared/utils/structureDisplayUtils'
import type {
  ComponentModelPiece,
  ComponentModelProduct,
  ComponentModelStructure,
  ComponentModelStructureNode,
} from '~/shared/types/inventory'
import type { ModelType } from '~/services/modelTypes'

interface ComponentCatalogType extends ModelType {
  structure: ComponentModelStructure | null
  customFields?: Array<Record<string, any>>
}

const route = useRoute()
const router = useRouter()
const { get } = useApi()

const { componentTypes, loadComponentTypes, loadingComponentTypes: loadingTypes } = useComponentTypes()
const { pieceTypes, loadPieceTypes } = usePieceTypes()
const { productTypes, loadProductTypes } = useProductTypes()
const {
  createComposant,
  composants: componentCatalogRef,
  loading: componentsLoading,
} = useComposants()
const {
  pieces: pieceCatalogRef,
  loading: piecesLoading,
} = usePieces()
const {
  products: productCatalogRef,
  loading: productsLoading,
} = useProducts()
const toast = useToast()
const { upsertCustomFieldValue, updateCustomFieldValue } = useCustomFields()
const { uploadDocuments } = useDocuments()
const { canEdit } = usePermissions()

const selectedTypeId = ref<string>(typeof route.query.typeId === 'string' ? route.query.typeId : '')
const submitting = ref(false)
const creationForm = reactive({
  name: '' as string,
  description: '' as string,
  reference: '' as string,
  constructeurIds: [] as string[],
  prix: '' as string,
})
const lastSuggestedName = ref('')
const customFieldInputs = ref<CustomFieldInput[]>([])
const structureAssignments = ref<StructureAssignmentNode | null>(null)
const selectedDocuments = ref<File[]>([])
const uploadingDocuments = ref(false)

const availablePieces = computed(() => pieceCatalogRef.value ?? [])
const availableProducts = computed(() => productCatalogRef.value ?? [])
const availableComponents = computed(() => componentCatalogRef.value ?? [])
const structureDataLoading = computed(
  () => piecesLoading.value || componentsLoading.value || productsLoading.value,
)

const fetchedPieceTypeMap = ref<Record<string, string>>({})
const pieceTypeLabelMap = computed(() =>
  buildTypeLabelMap(pieceTypes.value, fetchedPieceTypeMap.value),
)
const productTypeLabelMap = computed(() =>
  buildTypeLabelMap(productTypes.value),
)
const componentTypeLabelMap = computed(() =>
  buildTypeLabelMap(componentTypes.value),
)

watch(
  () => route.query.typeId,
  (value) => {
    if (typeof value === 'string') {
      selectedTypeId.value = value
    }
  },
)

watch(selectedTypeId, (id) => {
  const current = typeof route.query.typeId === 'string' ? route.query.typeId : ''
  if ((id || '') === current) {
    return
  }
  const nextQuery = { ...route.query }
  if (id) {
    nextQuery.typeId = id
  } else {
    delete nextQuery.typeId
  }
  router.replace({ path: route.path, query: nextQuery }).catch(() => {})
})

const componentTypeList = computed<ComponentCatalogType[]>(() =>
  (componentTypes.value || [])
    .filter((item: any) => item?.category === 'COMPONENT') as ComponentCatalogType[],
)

const typeOptionLabel = (type?: ComponentCatalogType) =>
  type?.name || 'Catégorie'

const typeOptionDescription = (type?: ComponentCatalogType) =>
  type?.description ? String(type.description) : ''

const selectedType = computed(() => {
  if (!selectedTypeId.value) {
    return null
  }
  return componentTypeList.value.find((type) => type.id === selectedTypeId.value) ?? null
})

const selectedTypeStructure = computed<ComponentModelStructure | null>(() => {
  const structure = selectedType.value?.structure ?? null
  return structure ? normalizeStructureForEditor(structure) : null
})

watch(selectedType, (type) => {
  if (!type) {
    clearCreationForm()
    customFieldInputs.value = []
    structureAssignments.value = null
    return
  }
  if (!creationForm.name || creationForm.name === lastSuggestedName.value) {
    creationForm.name = type.name
  }
  lastSuggestedName.value = creationForm.name
  customFieldInputs.value = normalizeCustomFieldInputs(selectedTypeStructure.value)
  structureAssignments.value = initializeStructureAssignments(selectedTypeStructure.value)
})

const extractSubcomponents = (
  definition: ComponentModelStructure | ComponentModelStructureNode | null | undefined,
): ComponentModelStructureNode[] => {
  if (!definition || typeof definition !== 'object') {
    return []
  }
  const raw = Array.isArray((definition as any).subcomponents)
    ? (definition as any).subcomponents
    : Array.isArray((definition as any).subComponents)
      ? (definition as any).subComponents
      : []
  return raw.filter(
    (item: unknown): item is ComponentModelStructureNode =>
      !!item && typeof item === 'object',
  )
}

const extractPiecesFromNode = (
  definition: ComponentModelStructure | ComponentModelStructureNode | null | undefined,
): ComponentModelPiece[] => {
  if (!definition || typeof definition !== 'object') {
    return []
  }
  const raw = Array.isArray((definition as any).pieces)
    ? (definition as any).pieces
    : []
  return raw.filter(
    (item: unknown): item is ComponentModelPiece =>
      !!item && typeof item === 'object',
  )
}

const extractProductsFromNode = (
  definition: ComponentModelStructure | ComponentModelStructureNode | null | undefined,
): ComponentModelProduct[] => {
  if (!definition || typeof definition !== 'object') {
    return []
  }
  const raw = Array.isArray((definition as any).products)
    ? (definition as any).products
    : []
  return raw.filter(
    (item: unknown): item is ComponentModelProduct =>
      !!item && typeof item === 'object',
  )
}

const buildAssignmentNode = (
  definition: ComponentModelStructureNode | ComponentModelStructure,
  path: string,
): StructureAssignmentNode => {
  const pieces = extractPiecesFromNode(definition).map((piece, index) => ({
    path: `${path}:piece-${index}`,
    definition: piece,
    selectedPieceId: '',
  }))

  const products = extractProductsFromNode(definition).map((product, index) => ({
    path: `${path}:product-${index}`,
    definition: product,
    selectedProductId: '',
  }))

  const subcomponents = extractSubcomponents(definition).map(
    (child, index) => buildAssignmentNode(child, `${path}:sub-${index}`),
  )

  return {
    path,
    definition,
    selectedComponentId: '',
    pieces,
    products,
    subcomponents,
  }
}

const initializeStructureAssignments = (
  structure: ComponentModelStructure | null,
): StructureAssignmentNode | null => {
  if (!structure || typeof structure !== 'object') {
    return null
  }
  return buildAssignmentNode(structure, 'root')
}

const hasAssignments = (node: StructureAssignmentNode | null): boolean => {
  if (!node) {
    return false
  }
  if (node.pieces.length > 0 || node.products.length > 0 || node.subcomponents.length > 0) {
    return true
  }
  return node.subcomponents.some((child) => hasAssignments(child))
}

const structureHasRequirements = computed(() =>
  hasAssignments(structureAssignments.value),
)

const isAssignmentNodeComplete = (
  node: StructureAssignmentNode,
  isRootNode = false,
): boolean => {
  const piecesComplete = node.pieces.every(
    (piece) => !!piece.selectedPieceId && piece.selectedPieceId.length > 0,
  )
  const productsComplete = node.products.every(
    (product) => !!product.selectedProductId && product.selectedProductId.length > 0,
  )
  const subcomponentsComplete = node.subcomponents.every(
    (child) =>
      !!child.selectedComponentId &&
      child.selectedComponentId.length > 0 &&
      isAssignmentNodeComplete(child, false),
  )
  return (
    piecesComplete &&
    productsComplete &&
    subcomponentsComplete &&
    (isRootNode || !!node.selectedComponentId)
  )
}

const structureSelectionsComplete = computed(() => {
  if (!structureHasRequirements.value) {
    return true
  }
  if (structureDataLoading.value) {
    return false
  }
  if (!structureAssignments.value) {
    return false
  }
  return isAssignmentNodeComplete(structureAssignments.value, true)
})

const stripNullish = (input: Record<string, any>) =>
  Object.fromEntries(
    Object.entries(input).filter(
      ([, value]) => value !== null && value !== undefined && value !== '',
    ),
  )

const sanitizeStructureDefinition = (
  definition: ComponentModelStructureNode,
) =>
  stripNullish({
    alias: definition.alias ?? null,
    typeComposantId: definition.typeComposantId ?? null,
    typeComposantLabel: definition.typeComposantLabel ?? null,
    modelId: definition.modelId ?? null,
    familyCode: (definition as any).familyCode ?? null,
  })

const sanitizePieceDefinition = (definition: ComponentModelPiece) =>
  stripNullish({
    role: (definition as any).role ?? null,
    typePieceId: definition.typePieceId ?? null,
    typePieceLabel: definition.typePieceLabel ?? null,
    reference: definition.reference ?? null,
    familyCode: (definition as any).familyCode ?? null,
  })

const sanitizeProductDefinition = (definition: ComponentModelProduct) =>
  stripNullish({
    role: (definition as any).role ?? null,
    typeProductId: definition.typeProductId ?? null,
    typeProductLabel: (definition as any).typeProductLabel ?? null,
    reference: (definition as any).reference ?? null,
    familyCode: (definition as any).familyCode ?? null,
  })

const serializeStructureAssignments = (
  root: StructureAssignmentNode | null,
) => {
  if (!root) {
    return null
  }

  const serializeNode = (
    assignment: StructureAssignmentNode,
    isRootNode = false,
  ): Record<string, any> => {
    const serializedPieces = assignment.pieces
      .filter((piece) => !!piece.selectedPieceId)
      .map((piece) =>
        stripNullish({
          path: piece.path,
          definition: sanitizePieceDefinition(piece.definition),
          selectedPieceId: piece.selectedPieceId,
        }),
      )

    const serializedProducts = assignment.products
      .filter((product) => !!product.selectedProductId)
      .map((product) =>
        stripNullish({
          path: product.path,
          definition: sanitizeProductDefinition(product.definition),
          selectedProductId: product.selectedProductId,
        }),
      )

    const serializedSubcomponents = assignment.subcomponents
      .map((child) => serializeNode(child, false))
      .filter((child) => Object.keys(child).length > 0)

    const base: Record<string, any> = {
      path: assignment.path,
      definition: sanitizeStructureDefinition(assignment.definition),
    }

    if (!isRootNode) {
      base.selectedComponentId = assignment.selectedComponentId
    }
    if (serializedPieces.length) {
      base.pieces = serializedPieces
    }
    if (serializedProducts.length) {
      base.products = serializedProducts
    }
    if (serializedSubcomponents.length) {
      base.subcomponents = serializedSubcomponents
    }

    return stripNullish(base)
  }

  const serializedRoot = serializeNode(root, true)
  if (
    (!serializedRoot.pieces || serializedRoot.pieces.length === 0) &&
    (!serializedRoot.products || serializedRoot.products.length === 0) &&
    (!serializedRoot.subcomponents || serializedRoot.subcomponents.length === 0)
  ) {
    return null
  }
  return serializedRoot
}

const requiredCustomFieldsFilled = computed(() =>
  _requiredCustomFieldsFilled(customFieldInputs.value),
)

const canSubmit = computed(() => Boolean(
  canEdit.value &&
  selectedType.value &&
  creationForm.name &&
  requiredCustomFieldsFilled.value &&
  structureSelectionsComplete.value &&
  !submitting.value,
))

const resolvePieceLabel = (piece: Record<string, any>) =>
  _resolvePieceLabel(piece, pieceTypeLabelMap.value)

const resolveProductLabel = (product: Record<string, any>) =>
  _resolveProductLabel(product, productTypeLabelMap.value)

watch(
  selectedTypeStructure,
  (structure) => {
    const ids = getStructurePieces(structure)
      .map((piece: any) => piece?.typePieceId)
      .filter((id: any): id is string => typeof id === 'string' && id.trim().length > 0)
    if (!ids.length) {
      return
    }
    fetchModelTypeNames(Array.from(new Set(ids)), pieceTypeLabelMap.value, get)
      .then((additions) => {
        if (Object.keys(additions).length) {
          fetchedPieceTypeMap.value = { ...fetchedPieceTypeMap.value, ...additions }
        }
      })
      .catch(() => {})
  },
  { immediate: true },
)

const clearCreationForm = () => {
  creationForm.name = ''
  creationForm.description = ''
  creationForm.reference = ''
  creationForm.constructeurIds = []
  creationForm.prix = ''
  lastSuggestedName.value = ''
  structureAssignments.value = null
}

const submitCreation = async () => {
  if (!selectedType.value) {
    toast.showError('Sélectionnez une catégorie de composant.')
    return
  }
  const payload: Record<string, any> = {
    name: creationForm.name.trim(),
    typeComposantId: selectedType.value.id,
  }

  const description = creationForm.description.trim()
  if (description) {
    payload.description = description
  }

  const reference = creationForm.reference.trim()
  if (reference) {
    payload.reference = reference
  }

  if (creationForm.constructeurIds.length) {
    payload.constructeurIds = uniqueConstructeurIds(creationForm.constructeurIds)
  }

  const rawPrice = typeof creationForm.prix === 'string'
    ? creationForm.prix.trim()
    : creationForm.prix === null || creationForm.prix === undefined
      ? ''
      : String(creationForm.prix).trim()

  if (rawPrice) {
    const parsed = Number(rawPrice)
    if (!Number.isNaN(parsed)) {
      payload.prix = String(parsed)
    }
  }

  const rootProductSelection =
    structureAssignments.value?.products?.find(
      (product) => typeof product.selectedProductId === 'string' && product.selectedProductId.trim().length > 0,
    ) ?? null

  if (rootProductSelection?.selectedProductId) {
    payload.productId = rootProductSelection.selectedProductId.trim()
  }

  if (structureHasRequirements.value && !structureSelectionsComplete.value) {
    toast.showError('Complétez la sélection des pièces, produits et sous-composants.')
    return
  }

  const serializedStructure = structureHasRequirements.value
    ? serializeStructureAssignments(structureAssignments.value)
    : null

  if (serializedStructure) {
    payload.structure = serializedStructure
  }

  submitting.value = true
  try {
    const result = await createComposant(payload)
    if (result.success) {
      const createdComponent = result.data as Record<string, any>
      await _saveCustomFieldValues(
        'composant',
        createdComponent.id,
        [createdComponent?.typeComposant?.customFields],
        { customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
      )
      if (selectedDocuments.value.length && result.data?.id) {
        uploadingDocuments.value = true
        const uploadResult = await uploadDocuments(
          {
            files: selectedDocuments.value,
            context: { composantId: result.data.id },
          },
          { updateStore: false },
        )
        if (!uploadResult.success) {
          const message = uploadResult.error
            ? `Documents non ajoutés : ${uploadResult.error}`
            : 'Documents non ajoutés : une erreur est survenue.'
          toast.showError(message)
        }
        selectedDocuments.value = []
      }
      toast.showSuccess('Composant créé avec succès')
      await router.push('/component-catalog')
    } else if (result.error) {
      toast.showError(result.error)
    }
  } catch (error: any) {
    toast.showError(humanizeError(error?.message) || 'Impossible de créer le composant')
  } finally {
    submitting.value = false
    uploadingDocuments.value = false
  }
}

onMounted(async () => {
  await Promise.allSettled([
    loadComponentTypes(),
    loadPieceTypes(),
    loadProductTypes(),
  ])
})
</script>
