export type ComponentModelCustomFieldType = 'text' | 'number' | 'select' | 'boolean' | 'date'

export interface ComponentModelCustomField {
  name: string
  type: ComponentModelCustomFieldType
  required: boolean
  options?: string[]
  defaultValue?: string | null
  optionsText?: string
  id?: string
  customFieldId?: string
  orderIndex?: number
  machineContextOnly?: boolean
  key?: string
  value?: unknown
}

export interface ComponentModelPiece {
  typePieceId?: string
  typePieceLabel?: string
  typePiece?: { id?: string; name?: string; code?: string } | null
  reference?: string
  familyCode?: string
  role?: string
  quantity?: number
}

export interface ComponentModelProduct {
  typeProductId?: string
  typeProductLabel?: string
  typeProduct?: { id?: string; name?: string; code?: string } | null
  reference?: string
  familyCode?: string
  role?: string
}

export interface ComponentModelStructureNode {
  typeComposantId?: string
  typeComposantLabel?: string
  typeComposant?: { id?: string; name?: string }
  modelId?: string
  familyCode?: string
  alias?: string
  products?: ComponentModelProduct[]
  subcomponents: ComponentModelStructureNode[]
}

export interface ComponentModelStructure extends ComponentModelStructureNode {
  customFields: ComponentModelCustomField[]
  pieces: ComponentModelPiece[]
  products: ComponentModelProduct[]
}

export type PieceModelCustomFieldType = ComponentModelCustomFieldType

export interface PieceModelCustomField {
  name: string
  type: PieceModelCustomFieldType
  required: boolean
  options?: string[]
  orderIndex?: number
  machineContextOnly?: boolean
  key?: string
  value?: unknown
  defaultValue?: string | null
  id?: string
  customFieldId?: string
}

export interface PieceModelProduct {
  typeProductId?: string
  typeProductLabel?: string
  reference?: string
  familyCode?: string
  role?: string
}

export interface PieceModelStructure {
  customFields: PieceModelCustomField[]
  products?: PieceModelProduct[]
  [key: string]: unknown
}

export interface PieceModelStructureEditorField extends PieceModelCustomField {
  optionsText: string
}

export interface PieceModelStructureForEditor {
  customFields: PieceModelStructureEditorField[]
  products?: PieceModelProduct[]
  [key: string]: unknown
}

export type ProductModelCustomField = PieceModelCustomField
export type ProductModelStructure = PieceModelStructure

const FIELD_TYPES: ComponentModelCustomFieldType[] = ['text', 'number', 'select', 'boolean', 'date']

const isPlainObject = (value: unknown): value is Record<string, unknown> => {
  return value !== null && typeof value === 'object' && !Array.isArray(value)
}

const isNonEmptyString = (value: unknown): value is string => typeof value === 'string' && value.trim().length > 0

const ensureString = (value: unknown): string | undefined => {
  if (!isNonEmptyString(value)) {
    return undefined
  }
  return value.trim()
}

const validateCustomField = (
  value: unknown,
  issues: string[],
  path: string,
): ComponentModelCustomField | null => {
  if (!isPlainObject(value)) {
    issues.push(`${path}: expected object`)
    return null
  }

  const name = ensureString(value.name)
  if (!name) {
    issues.push(`${path}.name: valeur manquante`)
    return null
  }

  const type = isNonEmptyString(value.type) && FIELD_TYPES.includes(value.type as ComponentModelCustomFieldType)
    ? (value.type as ComponentModelCustomFieldType)
    : 'text'

  const required = !!value.required

  let options: string[] | undefined
  if (value.options !== undefined) {
    if (!Array.isArray(value.options)) {
      issues.push(`${path}.options: attendu tableau`)
    } else {
      const parsed = value.options.map(ensureString).filter((option): option is string => !!option)
      if (parsed.length) {
        options = parsed
      }
    }
  }

  return { name, type, required, ...(options ? { options } : {}) }
}

const validatePiece = (
  value: unknown,
  issues: string[],
  path: string,
): ComponentModelPiece | null => {
  if (!isPlainObject(value)) {
    issues.push(`${path}: expected object`)
    return null
  }

  const typePieceId = ensureString(value.typePieceId)
  const typePieceLabel = ensureString(value.typePieceLabel)
  const reference = ensureString(value.reference)
  const familyCode = ensureString(value.familyCode)
  const role = ensureString(value.role)
  const quantity = typeof value.quantity === 'number' && value.quantity >= 1 ? value.quantity : undefined

  if (!typePieceId && !typePieceLabel && !reference && !familyCode) {
    issues.push(`${path}: au moins un identifiant, une famille ou une référence de pièce est requis`)
    return null
  }

  return {
    ...(typePieceId ? { typePieceId } : {}),
    ...(typePieceLabel ? { typePieceLabel } : {}),
    ...(reference ? { reference } : {}),
    ...(familyCode ? { familyCode } : {}),
    ...(role ? { role } : {}),
    ...(quantity ? { quantity } : {}),
  }
}

const validateStructureNode = (
  value: unknown,
  issues: string[],
  path: string,
): ComponentModelStructureNode | null => {
  if (!isPlainObject(value)) {
    issues.push(`${path}: expected object`)
    return null
  }

  const typeComposantId = ensureString(value.typeComposantId)
  const typeComposantLabel = ensureString(value.typeComposantLabel)
  const modelId = ensureString(value.modelId)
  const familyCode = ensureString(value.familyCode)
  const alias = ensureString(value.alias)

  const rawSubcomponents = Array.isArray(value.subcomponents)
    ? value.subcomponents
    : []

  const subcomponents: ComponentModelStructureNode[] = []
  rawSubcomponents.forEach((subValue: unknown, index: number) => {
    const parsed = validateStructureNode(subValue, issues, `${path}.subcomponents[${index}]`)
    if (parsed) {
      subcomponents.push(parsed)
    }
  })

  return {
    ...(typeComposantId ? { typeComposantId } : {}),
    ...(typeComposantLabel ? { typeComposantLabel } : {}),
    ...(modelId ? { modelId } : {}),
    ...(familyCode ? { familyCode } : {}),
    ...(alias ? { alias } : {}),
    subcomponents,
  }
}

export interface ComponentModelStructureValidationSuccess {
  success: true
  data: ComponentModelStructure
}

export interface ComponentModelStructureValidationFailure {
  success: false
  issues: string[]
}

export type ComponentModelStructureValidationResult =
  | ComponentModelStructureValidationSuccess
  | ComponentModelStructureValidationFailure

export const componentModelStructureValidator = {
  safeParse(value: unknown): ComponentModelStructureValidationResult {
    const issues: string[] = []

    if (!isPlainObject(value)) {
      issues.push('Structure invalide: attendu un objet')
      return { success: false, issues }
    }

    const customFieldsInput = Array.isArray(value.customFields) ? value.customFields : []
    const piecesInput = Array.isArray(value.pieces) ? value.pieces : []

    const customFields: ComponentModelCustomField[] = []
    customFieldsInput.forEach((field, index) => {
      const parsed = validateCustomField(field, issues, `customFields[${index}]`)
      if (parsed) {
        customFields.push(parsed)
      }
    })

    const pieces: ComponentModelPiece[] = []
    piecesInput.forEach((piece, index) => {
      const parsed = validatePiece(piece, issues, `pieces[${index}]`)
      if (parsed) {
        pieces.push(parsed)
      }
    })

    const node = validateStructureNode(value, issues, 'structure')

    if (!node) {
      issues.push('Structure racine invalide')
      return { success: false, issues }
    }

    if (issues.length > 0) {
      return { success: false, issues }
    }

    return {
      success: true,
      data: {
        ...node,
        customFields,
        pieces,
        products: ((node as unknown) as Record<string, unknown>).products as ComponentModelProduct[] ?? [],
      },
    }
  },
  parse(value: unknown): ComponentModelStructure {
    const result = this.safeParse(value)
    if (!result.success) {
      throw new Error(result.issues.join('\n'))
    }
    return result.data
  },
}

export const createEmptyComponentModelStructure = (): ComponentModelStructure => ({
  customFields: [],
  pieces: [],
  products: [],
  subcomponents: [],
})

export const createEmptyPieceModelStructure = (): PieceModelStructure => ({
  customFields: [],
  products: [],
})

export const createEmptyProductModelStructure = (): ProductModelStructure => ({
  customFields: [],
})
