'use client'

import dynamic from 'next/dynamic'
import { useEffect, useMemo, useState } from 'react'

import { useI18n } from '@/components/providers/i18n-provider'
import { useAppMode } from '@/components/providers/app-mode-provider'
import { Segmented } from '@/components/ui/controls'
import { Badge } from '@/components/ui/display'
import { cn } from '@/lib/cn'
import {
  TwinScene2D,
  type PropagationStep,
  type SceneAssetState,
  type SceneEdge,
  type SceneNode,
  type SensorMarker,
  type TwinLayers,
  type TwinView,
} from './twin-scene-2d'

/**
 * Chooses how to draw the twin (§15, §71, §72).
 *
 * The isometric SVG scene is the default because it is instant, universal and printable.
 * WebGL is offered as a toggle where the browser supports it, is code-split so nobody
 * downloads three.js unless they ask for it, and falls back — with an explanation rather
 * than a blank panel — when it is unavailable.
 */

const TwinScene3D = dynamic(() => import('./twin-scene-3d'), {
  ssr: false,
  loading: function SceneLoading() {
    return (
      <div className="flex h-full min-h-[20rem] items-center justify-center text-xs text-text-muted">
        <span className="me-2 inline-block size-3 animate-spin rounded-full border-2 border-text-faint border-t-transparent" />
        Loading scene…
      </div>
    )
  },
})

/**
 * Probe for a usable WebGL context.
 *
 * Done once, on the client, with the context released immediately: creating and keeping
 * a probe context is a real way to exhaust the browser's context budget on a page that
 * later wants a real one.
 */
function detectWebGL(): boolean {
  if (typeof document === 'undefined') return false
  try {
    const canvas = document.createElement('canvas')
    const context =
      canvas.getContext('webgl2') ??
      canvas.getContext('webgl') ??
      canvas.getContext('experimental-webgl')
    if (!context) return false
    const lose = (context as WebGLRenderingContext).getExtension('WEBGL_lose_context')
    lose?.loseContext()
    return true
  } catch {
    return false
  }
}

export function TwinCanvas({
  nodes,
  edges,
  assets,
  radius,
  view,
  layers,
  selectedCode,
  onSelect,
  propagation,
  sensors,
  className,
  allow3D = true,
}: {
  nodes: SceneNode[]
  edges: SceneEdge[]
  assets: SceneAssetState[]
  radius: number
  view: TwinView
  layers: TwinLayers
  selectedCode?: string | null
  onSelect?: (code: string) => void
  propagation?: PropagationStep[]
  sensors?: SensorMarker[]
  className?: string
  allow3D?: boolean
}) {
  const { t } = useI18n()
  const { motion } = useAppMode()
  const [webgl, setWebgl] = useState<boolean | null>(null)
  const [dimension, setDimension] = useState<'2d' | '3d'>('2d')

  useEffect(() => {
    setWebgl(detectWebGL())
  }, [])

  const canUse3D = allow3D && webgl === true
  const showing3D = canUse3D && dimension === '3d'

  const options = useMemo(
    () =>
      [
        { value: '2d' as const, label: t('twin.view2d') },
        ...(canUse3D ? [{ value: '3d' as const, label: t('twin.view3d') }] : []),
      ],
    [canUse3D, t],
  )

  return (
    <div className={cn('relative', className)}>
      <div className="absolute inset-x-3 top-3 z-10 flex flex-wrap items-start justify-between gap-2">
        <Segmented
          ariaLabel={t('twin.title')}
          size="sm"
          value={dimension}
          onChange={(next) => setDimension(next)}
          options={options}
        />
        {webgl === false ? (
          <Badge tone="muted" className="max-w-[18rem] text-[10px] leading-snug">
            {t('twin.webglFallback')}
          </Badge>
        ) : null}
      </div>

      {showing3D ? (
        <div className="h-[26rem] w-full overflow-hidden rounded-[--radius-panel] sm:h-[32rem]">
          <TwinScene3D
            nodes={nodes}
            edges={edges}
            assets={assets}
            view={view}
            selectedCode={selectedCode}
            onSelect={onSelect}
            radius={radius}
            reducedMotion={motion === 'reduced'}
          />
        </div>
      ) : (
        <TwinScene2D
          nodes={nodes}
          edges={edges}
          assets={assets}
          view={view}
          layers={layers}
          selectedCode={selectedCode}
          onSelect={onSelect}
          propagation={propagation}
          sensors={sensors}
        />
      )}
    </div>
  )
}
