'use client'

import { Component, useEffect, useState } from 'react'
import type { ComponentType, ReactNode } from 'react'
import type { ModelHierarchyNode, ModelStats, SceneViewerProps } from '@/lib/client-types'

interface ModelViewerProps {
  url: string
  assetUrls: Record<string, string>
  filename: string
  size: string
}

const VIEWER_FRAME_CLASS = 'w-full h-[450px] bg-black-800 border border-white/20 rounded-xl overflow-hidden'
const CENTERED_VIEWER_FRAME_CLASS = `${VIEWER_FRAME_CLASS} flex items-center justify-center`

const MODEL_STAT_ROWS = [
  { label: 'Draw calls', getValue: (stats: ModelStats) => stats.drawCalls },
  { label: 'Children', getValue: (stats: ModelStats) => stats.childObjects },
  { label: 'Meshes', getValue: (stats: ModelStats) => stats.meshes },
  { label: 'Triangles', getValue: (stats: ModelStats) => stats.triangles.toLocaleString('fr-FR') },
  { label: 'Materials', getValue: (stats: ModelStats) => stats.materials },
  { label: 'Textures', getValue: (stats: ModelStats) => stats.textures },
] satisfies Array<{
  label: string
  getValue: (stats: ModelStats) => number | string
}>

function getPreviewErrorMessage(error: unknown) {
  return error instanceof Error ? error.message : 'Erreur preview inconnue'
}

function PreviewFallback({ message }: { message?: string }) {
  return (
    <div className="flex h-full w-full items-center justify-center px-6 text-center">
      <div className="max-w-sm space-y-2">
        <p className="text-sm font-medium text-gray-300">Preview 3D indisponible pour ce modele.</p>
        <p className="text-xs text-gray-500">
          L&apos;upload reste possible. {message ? `Detail technique : ${message}` : ''}
        </p>
      </div>
    </div>
  )
}

function formatTransformValue(value: number) {
  return Number.isInteger(value) ? String(value) : value.toFixed(4).replace(/0+$/, '').replace(/\.$/, '')
}

function formatTransform(values: [number, number, number]) {
  return `[${values.map(formatTransformValue).join(', ')}]`
}

function countHierarchyNodes(node: ModelHierarchyNode): number {
  return 1 + node.children.reduce((count, child) => count + countHierarchyNodes(child), 0)
}

function HierarchyTree({
  node,
  depth = 0,
}: {
  node: ModelHierarchyNode
  depth?: number
}) {
  const hasChildren = node.children.length > 0

  return (
    <div>
      <div
        className="grid grid-cols-[minmax(0,1fr)_auto] items-start gap-3 py-1 text-xs"
        style={{ paddingLeft: `${depth * 14}px` }}
      >
        <div className="min-w-0">
          <div className="flex min-w-0 items-center gap-1.5">
            <span className="w-3 shrink-0 text-gray-500">{hasChildren ? 'v' : '-'}</span>
            <span className="truncate font-semibold text-gray-200">{node.name}</span>
            <span className="shrink-0 text-[10px] text-gray-500">{node.type}</span>
          </div>
          <div className="ml-4 mt-0.5 flex flex-wrap gap-x-4 gap-y-1 font-mono text-[10px] text-gray-600">
            <span>pos {formatTransform(node.position)}</span>
            <span>rot {formatTransform(node.rotation)}</span>
          </div>
        </div>
        <span className={`rounded-full border px-2 py-0.5 text-[10px] ${
          node.visible
            ? 'border-white/10 bg-white/5 text-gray-400'
            : 'border-red-500/20 bg-red-500/10 text-red-300'
        }`}
        >
          {node.visible ? 'Visible' : 'Hidden'}
        </span>
      </div>
      {hasChildren && (
        <div className="border-l border-white/10">
          {node.children.map((child) => (
            <HierarchyTree key={child.id} node={child} depth={depth + 1} />
          ))}
        </div>
      )}
    </div>
  )
}

function HierarchyPanel({
  hierarchy,
  onClose,
}: {
  hierarchy: ModelHierarchyNode
  onClose: () => void
}) {
  const nodeCount = countHierarchyNodes(hierarchy)

  return (
    <div className="absolute inset-y-3 right-3 z-30 flex w-[min(22rem,calc(100%-1.5rem))] flex-col overflow-hidden rounded-xl border border-white/15 bg-black-900/90 text-gray-300 shadow-2xl backdrop-blur">
      <div className="flex items-center justify-between border-b border-white/10 px-3 py-2">
        <div>
          <p className="text-xs font-semibold text-gray-100">Hierarchy</p>
          <p className="text-[10px] text-gray-600">{nodeCount} nodes</p>
        </div>
        <button
          type="button"
          onClick={onClose}
          className="rounded-full border border-white/10 px-2 py-1 text-xs text-gray-400 transition hover:bg-white/10 hover:text-gray-100"
        >
          Close
        </button>
      </div>
      <div className="flex-1 overflow-auto px-3 py-2">
        <HierarchyTree node={hierarchy} />
      </div>
    </div>
  )
}

function ModelStatsPanel({ stats }: { stats: ModelStats }) {
  return (
    <div className="flex flex-col gap-1.5 rounded-lg border border-white/10 bg-black-900/75 p-2 text-xs text-gray-300 backdrop-blur">
      {MODEL_STAT_ROWS.map(({ label, getValue }) => (
        <span key={label} className="flex justify-between gap-3">
          <span className="text-gray-500">{label}</span>
          <span className="font-mono text-gray-200">{getValue(stats)}</span>
        </span>
      ))}
    </div>
  )
}

class PreviewErrorBoundary extends Component<
  { children: ReactNode },
  { message: string | null }
> {
  state = { message: null }

  static getDerivedStateFromError(error: unknown) {
    return { message: getPreviewErrorMessage(error) }
  }

  componentDidCatch(error: unknown) {
    console.error('[ERROR] Preview 3D indisponible', error)
  }

  render() {
    if (this.state.message) {
      return <PreviewFallback message={this.state.message} />
    }

    return this.props.children
  }
}

export default function ModelViewer({ url, assetUrls, filename, size }: ModelViewerProps) {
  const canPreview = filename.toLowerCase().endsWith('.gltf')
  const [stats, setStats] = useState<ModelStats | null>(null)
  const [hierarchy, setHierarchy] = useState<ModelHierarchyNode | null>(null)
  const [hierarchyOpen, setHierarchyOpen] = useState(false)
  const [sceneError, setSceneError] = useState<string | null>(null)
  const [Scene, setScene] = useState<ComponentType<SceneViewerProps> | null>(null)

  useEffect(() => {
    if (!canPreview) return

    let cancel = false
    setSceneError(null)
    setStats(null)
    setHierarchy(null)
    setHierarchyOpen(false)

    import('./SceneViewer')
      .then((mod) => {
        if (!cancel) setScene(() => mod.default)
      })
      .catch((error: unknown) => {
        if (!cancel) setSceneError(getPreviewErrorMessage(error))
      })

    return () => { cancel = true }
  }, [canPreview, url])

  if (!canPreview) {
    return (
      <div className={CENTERED_VIEWER_FRAME_CLASS}>
        <p className="text-sm text-gray-400 px-6 text-center">
          La preview 3D locale n&apos;est pas disponible pour les dossiers <span className="font-mono">model.gltf</span> avec fichiers associes.
        </p>
      </div>
    )
  }

  if (!Scene) {
    return (
      <div className={CENTERED_VIEWER_FRAME_CLASS}>
        <div className="w-6 h-6 border-2 border-gray-500 border-t-gray-300 rounded-full animate-spin" />
      </div>
    )
  }

  return (
    <div className={`${VIEWER_FRAME_CLASS} relative`}>
      <div className="absolute top-3 left-3 z-10 flex items-center gap-2">
        <span className="text-xs text-gray-400 font-mono bg-black-900/60 px-2 py-1 rounded">
          {filename}
        </span>
        <span className="text-xs text-gray-500 bg-black-900/60 px-2 py-1 rounded">
          {size}
        </span>
      </div>
      {stats && (
        <div className="absolute top-3 right-3 z-20 flex w-44 flex-col gap-2">
          <ModelStatsPanel stats={stats} />
          <button
            type="button"
            disabled={!hierarchy}
            onClick={() => setHierarchyOpen((open) => !open)}
            className="rounded-lg border border-white/10 bg-black-900/75 px-3 py-1.5 text-xs font-medium text-gray-300 backdrop-blur transition hover:bg-white/10 hover:text-gray-100 disabled:cursor-not-allowed disabled:opacity-50"
          >
            Hierarchy
          </button>
        </div>
      )}
      {hierarchy && hierarchyOpen && (
        <HierarchyPanel hierarchy={hierarchy} onClose={() => setHierarchyOpen(false)} />
      )}
      {sceneError ? (
        <PreviewFallback message={sceneError} />
      ) : (
        <PreviewErrorBoundary key={url}>
          <Scene
            url={url}
            assetUrls={assetUrls}
            onStatsReady={setStats}
            onHierarchyReady={setHierarchy}
          />
        </PreviewErrorBoundary>
      )}
    </div>
  )
}
