'use client'

import { useMemo, useState } from 'react'
import {
  carnotCycleSimulator as sim,
  fridgeLoadFromModeLoad,
  modeLoadFromFridgeLoad,
  resolveCarnotPhysics,
  type CarnotMode,
  type CarnotQuantity,
} from '@/lib/simulators/carnot-cycle'
import { intentColor } from '@/lib/interactive-demo/intent-colors'
import { useDarkMode } from '@/components/interactive-demo/demo-speak'
import { SimHeading, SimOutputCard, SimSlider } from './sim-controls'
import { cn } from '@/lib/utils'

const SVG_W = 360
const SVG_H = 270

type TempUnit = 'K' | 'C' | 'F'

const TEMP_UNITS: { id: TempUnit; label: string }[] = [
  { id: 'K', label: 'K' },
  { id: 'C', label: '°C' },
  { id: 'F', label: '°F' },
]

const MODES: { id: CarnotMode; fr: string; en: string }[] = [
  { id: 'fridge', fr: 'Frigo', en: 'Fridge' },
  { id: 'heat_pump', fr: 'PAC', en: 'Heat pump' },
  { id: 'engine', fr: 'Moteur', en: 'Engine' },
]

const QTY: { id: CarnotQuantity; fr: string; en: string }[] = [
  { id: 'energy', fr: 'Énergie (kJ)', en: 'Energy (kJ)' },
  { id: 'power', fr: 'Puissance (W)', en: 'Power (W)' },
]

function kelvinToDisplay(k: number, unit: TempUnit): number {
  if (unit === 'C') return k - 273.15
  if (unit === 'F') return (k * 9) / 5 - 459.67
  return k
}

function displayToKelvin(v: number, unit: TempUnit): number {
  if (unit === 'C') return v + 273.15
  if (unit === 'F') return ((v + 459.67) * 5) / 9
  return v
}

function formatTemp(k: number, unit: TempUnit): string {
  const v = kelvinToDisplay(k, unit)
  if (unit === 'K') return `${Math.round(v)} K`
  if (unit === 'C') {
    const r = Math.round(v * 10) / 10
    return `${Number.isInteger(r) ? r : r.toFixed(1)} °C`
  }
  return `${Math.round(v)} °F`
}

function tempSliderMeta(
  param: { min: number; max: number; step: number },
  unit: TempUnit
): { min: number; max: number; step: number; unitLabel: string } {
  if (unit === 'K') {
    return { min: param.min, max: param.max, step: param.step, unitLabel: 'K' }
  }
  if (unit === 'C') {
    return {
      min: Math.round((param.min - 273.15) * 10) / 10,
      max: Math.round((param.max - 273.15) * 10) / 10,
      step: 0.5,
      unitLabel: '°C',
    }
  }
  return {
    min: Math.round(((param.min * 9) / 5 - 459.67) * 10) / 10,
    max: Math.round(((param.max * 9) / 5 - 459.67) * 10) / 10,
    step: 1,
    unitLabel: '°F',
  }
}

function formatQty(v: number): string {
  if (!Number.isFinite(v)) return '—'
  if (Math.abs(v - Math.round(v)) < 0.05) return String(Math.round(v))
  return v.toFixed(1)
}

function Segmented<T extends string>({
  value,
  onChange,
  options,
  ariaLabel,
}: {
  value: T
  onChange: (v: T) => void
  options: { id: T; label: string }[]
  ariaLabel: string
}) {
  return (
    <div
      className="inline-flex max-w-full flex-wrap rounded-lg border border-border/60 bg-background/80 p-0.5"
      role="group"
      aria-label={ariaLabel}
    >
      {options.map((o) => (
        <button
          key={o.id}
          type="button"
          onClick={() => onChange(o.id)}
          className={cn(
            'rounded-md px-2 py-1 text-[11px] font-semibold transition-colors',
            value === o.id
              ? 'bg-foreground text-background'
              : 'text-muted-foreground hover:text-foreground'
          )}
          aria-pressed={value === o.id}
        >
          {o.label}
        </button>
      ))}
    </div>
  )
}

/** Arrowhead + shaft, thickness ∝ |value|/maxRef. */
function FlowArrow({
  x1,
  y1,
  x2,
  y2,
  value,
  maxRef,
  color,
  label,
  unit,
  labelSide = 'right',
}: {
  x1: number
  y1: number
  x2: number
  y2: number
  value: number
  maxRef: number
  color: string
  label: string
  unit?: string
  labelSide?: 'left' | 'right' | 'above' | 'below'
}) {
  const mag = Math.max(0, value)
  const t = 2.2 + (mag / Math.max(1, maxRef)) * 10
  const dx = x2 - x1
  const dy = y2 - y1
  const len = Math.hypot(dx, dy) || 1
  const ux = dx / len
  const uy = dy / len
  const headLen = Math.min(14, Math.max(9, 7 + t * 0.45))
  const headHalf = Math.min(7, 3.2 + t * 0.35)
  const bx = x2 - ux * headLen
  const by = y2 - uy * headLen
  const px = -uy
  const py = ux
  const mx = (x1 + bx) / 2
  const my = (y1 + by) / 2
  const labelGap = 11 + t * 0.45

  let lx = mx
  let ly = my
  let textAnchor: 'start' | 'middle' | 'end' = 'middle'
  if (labelSide === 'above') ly = my - labelGap
  else if (labelSide === 'below') ly = my + labelGap
  else if (labelSide === 'right') {
    lx = mx + labelGap
    textAnchor = 'start'
  } else {
    lx = mx - labelGap
    textAnchor = 'end'
  }

  return (
    <g>
      <line
        x1={x1}
        y1={y1}
        x2={bx}
        y2={by}
        stroke={color}
        strokeWidth={t}
        strokeLinecap="round"
        opacity={0.9}
      />
      <polygon
        points={`${x2},${y2} ${bx + px * headHalf},${by + py * headHalf} ${bx - px * headHalf},${by - py * headHalf}`}
        fill={color}
        opacity={0.95}
      />
      <text
        x={lx}
        y={ly}
        fontSize={11}
        fill={color}
        fontWeight={600}
        dominantBaseline="middle"
        textAnchor={textAnchor}
      >
        {label} {formatQty(value)}
        {unit ? ` ${unit}` : ''}
      </text>
    </g>
  )
}

export function CarnotCycleView({
  preset,
  disclaimer,
  lang,
}: {
  preset?: Record<string, number>
  title?: string
  disclaimer?: string
  lang: string
}) {
  const fr = lang.startsWith('fr')
  const dark = useDarkMode()
  const [tempUnit, setTempUnit] = useState<TempUnit>('K')
  const [mode, setMode] = useState<CarnotMode>('fridge')
  const [qty, setQty] = useState<CarnotQuantity>('energy')
  const [values, setValues] = useState<Record<string, number>>(() => {
    const env: Record<string, number> = {}
    for (const p of sim.params) env[p.id] = preset?.[p.id] ?? p.defaultValue
    return env
  })

  const phys = useMemo(
    () =>
      resolveCarnotPhysics(
        values.t_cold,
        values.t_hot,
        modeLoadFromFridgeLoad(values.t_cold, values.t_hot, values.q_cold, mode),
        mode
      ),
    [values.t_cold, values.t_hot, values.q_cold, mode]
  )

  const unitE = qty === 'energy' ? 'kJ' : 'W'
  const workName =
    qty === 'energy'
      ? fr
        ? 'Travail'
        : 'Work'
      : fr
        ? 'Puissance'
        : 'Power'

  const maxRef = Math.max(phys.qh || 0, phys.qc || 0, phys.w || 0, 1)
  const cHot = intentColor('warning', dark)
  const cCold = intentColor('cache', dark)
  const cWork = intentColor('compute', dark)
  const text = dark ? '#e4e4e7' : '#27272a'

  const cx = SVG_W / 2 - 20
  const hotY = 28
  const coldY = SVG_H - 42
  const midY = SVG_H / 2 - 4
  const r = 32
  const qOffset = 42
  const isEngine = mode === 'engine'

  const loadMeta = useMemo(() => {
    if (mode === 'fridge') {
      return {
        symbol: qty === 'energy' ? 'Q_c' : '\\dot{Q}_c',
        label: fr ? 'Chaleur extraite (froid)' : 'Heat extracted (cold)',
        hint: fr
          ? 'Charge utile du réfrigérateur'
          : 'Useful fridge cooling load',
      }
    }
    if (mode === 'heat_pump') {
      return {
        symbol: qty === 'energy' ? 'Q_h' : '\\dot{Q}_h',
        label: fr ? 'Chaleur fournie (chaud)' : 'Heat delivered (hot)',
        hint: fr ? 'Charge utile de la PAC' : 'Useful heat-pump output',
      }
    }
    return {
      symbol: qty === 'energy' ? 'Q_h' : '\\dot{Q}_h',
      label: fr ? 'Chaleur absorbée (chaud)' : 'Heat absorbed (hot)',
      hint: fr ? 'Entrée thermique du moteur' : 'Engine heat input',
    }
  }, [mode, qty, fr])

  const modeLoad = modeLoadFromFridgeLoad(
    values.t_cold,
    values.t_hot,
    values.q_cold,
    mode
  )

  const workSym = qty === 'energy' ? 'W' : 'P'
  const qLabel = (base: 'c' | 'h') => (qty === 'energy' ? `Q_${base}` : `Q̇_${base}`)

  const lawLine = !phys.ok
    ? fr
      ? 'Il faut T_h > T_c (températures absolues).'
      : 'Need T_h > T_c (absolute temperatures).'
    : fr
      ? `1ᵉʳ principe : ${qLabel('h')} = ${qLabel('c')} + ${workSym} → ${formatQty(phys.qh)} = ${formatQty(phys.qc)} + ${formatQty(phys.w)} ${unitE}`
      : `1st law: ${qLabel('h')} = ${qLabel('c')} + ${workSym} → ${formatQty(phys.qh)} = ${formatQty(phys.qc)} + ${formatQty(phys.w)} ${unitE}`

  return (
    <div>
      <div className="mb-3 flex flex-wrap items-center gap-2">
        <Segmented
          value={mode}
          onChange={setMode}
          ariaLabel={fr ? 'Mode machine' : 'Machine mode'}
          options={MODES.map((m) => ({ id: m.id, label: fr ? m.fr : m.en }))}
        />
        <Segmented
          value={qty}
          onChange={setQty}
          ariaLabel={fr ? 'Énergie ou puissance' : 'Energy or power'}
          options={QTY.map((q) => ({ id: q.id, label: fr ? q.fr : q.en }))}
        />
        <Segmented
          value={tempUnit}
          onChange={setTempUnit}
          ariaLabel={fr ? 'Unité de température' : 'Temperature unit'}
          options={TEMP_UNITS.map((u) => ({ id: u.id, label: u.label }))}
        />
      </div>

      <div className="grid gap-5 md:grid-cols-[minmax(0,1.1fr)_minmax(0,1fr)]">
        <div>
          <svg
            viewBox={`0 0 ${SVG_W} ${SVG_H}`}
            className="mx-auto w-full max-w-md"
            role="img"
            aria-label={fr ? sim.title.fr : sim.title.en}
          >
            <rect
              x={cx - 105}
              y={hotY - 18}
              width={210}
              height={34}
              rx={10}
              fill={`${cHot}14`}
              stroke={cHot}
              strokeWidth={1.5}
            />
            <text
              x={cx}
              y={hotY}
              textAnchor="middle"
              fontSize={11.5}
              fontWeight={600}
              fill={cHot}
              dominantBaseline="middle"
            >
              {fr ? 'Source chaude' : 'Hot'} · T_h = {formatTemp(values.t_hot, tempUnit)}
            </text>

            <rect
              x={cx - 105}
              y={coldY - 18}
              width={210}
              height={34}
              rx={10}
              fill={`${cCold}14`}
              stroke={cCold}
              strokeWidth={1.5}
            />
            <text
              x={cx}
              y={coldY}
              textAnchor="middle"
              fontSize={11.5}
              fontWeight={600}
              fill={cCold}
              dominantBaseline="middle"
            >
              {fr ? 'Source froide' : 'Cold'} · T_c ={' '}
              {formatTemp(values.t_cold, tempUnit)}
            </text>

            <circle
              cx={cx}
              cy={midY}
              r={r}
              fill={dark ? '#141820' : '#ffffff'}
              stroke={text}
              strokeWidth={1.5}
            />
            <text
              x={cx}
              y={midY}
              textAnchor="middle"
              fontSize={11}
              fontWeight={600}
              fill={text}
              dominantBaseline="middle"
            >
              {fr ? 'Machine' : 'Engine'}
            </text>

            {phys.ok && !isEngine ? (
              <>
                {/* Fridge / PAC: Qc↑ into machine, W→ into machine, Qh↑ to hot */}
                <FlowArrow
                  x1={cx - qOffset}
                  y1={coldY - 22}
                  x2={cx - qOffset}
                  y2={midY + r + 2}
                  value={phys.qc}
                  maxRef={maxRef}
                  color={cCold}
                  label={qLabel('c')}
                  unit={unitE}
                  labelSide="left"
                />
                <FlowArrow
                  x1={cx + qOffset}
                  y1={midY - r - 2}
                  x2={cx + qOffset}
                  y2={hotY + 22}
                  value={phys.qh}
                  maxRef={maxRef}
                  color={cHot}
                  label={qLabel('h')}
                  unit={unitE}
                />
                <FlowArrow
                  x1={cx + r + 78}
                  y1={midY}
                  x2={cx + r + 4}
                  y2={midY}
                  value={phys.w}
                  maxRef={maxRef}
                  color={cWork}
                  label={workSym}
                  unit={unitE}
                  labelSide="above"
                />
              </>
            ) : null}

            {phys.ok && isEngine ? (
              <>
                {/* Engine: Qh↓ from hot into machine, W→ out, Qc↓ to cold */}
                <FlowArrow
                  x1={cx + qOffset}
                  y1={hotY + 22}
                  x2={cx + qOffset}
                  y2={midY - r - 2}
                  value={phys.qh}
                  maxRef={maxRef}
                  color={cHot}
                  label={qLabel('h')}
                  unit={unitE}
                />
                <FlowArrow
                  x1={cx - qOffset}
                  y1={midY + r + 2}
                  x2={cx - qOffset}
                  y2={coldY - 22}
                  value={phys.qc}
                  maxRef={maxRef}
                  color={cCold}
                  label={qLabel('c')}
                  unit={unitE}
                  labelSide="left"
                />
                <FlowArrow
                  x1={cx + r + 4}
                  y1={midY}
                  x2={cx + r + 78}
                  y2={midY}
                  value={phys.w}
                  maxRef={maxRef}
                  color={cWork}
                  label={workSym}
                  unit={unitE}
                  labelSide="above"
                />
              </>
            ) : null}

            <text x={10} y={SVG_H - 10} fontSize={10} fill={dark ? '#8b8b93' : '#6b7280'}>
              {fr
                ? qty === 'energy'
                  ? 'Épaisseur ∝ énergie (kJ) — W = travail (pas le watt)'
                  : 'Épaisseur ∝ puissance — P et W (watt) = même unité ici'
                : qty === 'energy'
                  ? 'Thickness ∝ energy (kJ) — W = work (not the watt)'
                  : 'Thickness ∝ power — P uses watts'}
            </text>
          </svg>
          <p className="mt-2 text-center text-[11px] text-muted-foreground">{lawLine}</p>
          {phys.ok && phys.entropyOk ? (
            <p className="mt-1 text-center text-[10px] text-muted-foreground/80">
              {fr
                ? '2ᵉ principe (réversible) : Q_c/T_c = Q_h/T_h'
                : '2nd law (reversible): Q_c/T_c = Q_h/T_h'}
            </p>
          ) : null}
        </div>

        <div className="space-y-3">
          <div>
            <SimHeading>{fr ? 'Paramètres' : 'Parameters'}</SimHeading>
            <div className="space-y-2">
              {sim.params
                .filter((p) => p.id === 't_cold' || p.id === 't_hot')
                .map((p) => {
                  const meta = tempSliderMeta(p, tempUnit)
                  const displayVal = kelvinToDisplay(values[p.id], tempUnit)
                  return (
                    <SimSlider
                      key={`${p.id}-${tempUnit}`}
                      symbol={p.symbol}
                      label={fr ? p.label.fr : p.label.en}
                      unit={meta.unitLabel}
                      min={meta.min}
                      max={meta.max}
                      step={meta.step}
                      value={
                        tempUnit === 'K'
                          ? Math.round(displayVal)
                          : Math.round(displayVal * 10) / 10
                      }
                      intent={p.intent}
                      onChange={(v) => {
                        const k = displayToKelvin(v, tempUnit)
                        const clamped = Math.min(p.max, Math.max(p.min, k))
                        setValues((s) => ({ ...s, [p.id]: clamped }))
                      }}
                    />
                  )
                })}

              <SimSlider
                key={`load-${mode}-${qty}`}
                symbol={loadMeta.symbol}
                label={loadMeta.label}
                unit={unitE}
                min={10}
                max={500}
                step={5}
                value={Math.round(modeLoad * 10) / 10}
                intent="flow"
                onChange={(v) => {
                  const fridgeQc = fridgeLoadFromModeLoad(
                    values.t_cold,
                    values.t_hot,
                    v,
                    mode
                  )
                  setValues((s) => ({
                    ...s,
                    q_cold: Math.min(500, Math.max(10, fridgeQc)),
                  }))
                }}
              />
              <p className="px-1 text-[10px] text-muted-foreground">{loadMeta.hint}</p>
            </div>
          </div>

          <div>
            <SimHeading>
              {fr ? 'Résultats (limites de Carnot)' : 'Results (Carnot limits)'}
            </SimHeading>
            <div className="grid grid-cols-2 gap-2">
              {mode === 'fridge' || mode === 'heat_pump' ? (
                <>
                  <SimOutputCard
                    symbol={String.raw`\mathrm{COP}_{R}`}
                    label={fr ? 'COP frigo' : 'Fridge COP'}
                    value={phys.copR}
                    intent="output"
                    digits={2}
                  />
                  <SimOutputCard
                    symbol={String.raw`\mathrm{COP}_{HP}`}
                    label={fr ? 'COP PAC' : 'Heat-pump COP'}
                    value={phys.copHP}
                    intent="output"
                    digits={2}
                  />
                </>
              ) : (
                <SimOutputCard
                  symbol={String.raw`\eta`}
                  label={fr ? 'Rendement moteur' : 'Engine efficiency'}
                  value={phys.eta * 100}
                  unit="%"
                  intent="highlight"
                  digits={1}
                />
              )}
              <SimOutputCard
                symbol={workSym}
                label={
                  isEngine
                    ? fr
                      ? `${workName} fourni`
                      : `${workName} out`
                    : fr
                      ? `${workName} minimale`
                      : `Minimum ${workName.toLowerCase()}`
                }
                value={phys.w}
                unit={unitE}
                intent="compute"
                digits={1}
              />
              <SimOutputCard
                symbol={
                  qty === 'energy' ? String.raw`Q_h` : String.raw`\dot{Q}_h`
                }
                label={
                  isEngine
                    ? fr
                      ? 'Chaleur absorbée'
                      : 'Heat absorbed'
                    : fr
                      ? 'Chaleur côté chaud'
                      : 'Hot-side heat'
                }
                value={phys.qh}
                unit={unitE}
                intent="flow"
                digits={1}
              />
              <SimOutputCard
                symbol={
                  qty === 'energy' ? String.raw`Q_c` : String.raw`\dot{Q}_c`
                }
                label={
                  isEngine
                    ? fr
                      ? 'Chaleur rejetée (froid)'
                      : 'Heat rejected (cold)'
                    : fr
                      ? 'Chaleur côté froid'
                      : 'Cold-side heat'
                }
                value={phys.qc}
                unit={unitE}
                intent="cache"
                digits={1}
              />
            </div>
            <p className="mt-2 text-[10px] leading-relaxed text-muted-foreground">
              {qty === 'energy'
                ? fr
                  ? 'W = travail (énergie en kJ), pas le watt. Passe en « Puissance (W) » pour raisonner en watts.'
                  : 'W = work (energy in kJ), not the watt. Switch to “Power (W)” to use watts.'
                : fr
                  ? 'Mode puissance : P, Q̇_c et Q̇_h sont en watts (W). Les COP / η restent sans unité.'
                  : 'Power mode: P, Q̇_c and Q̇_h are in watts (W). COP / η stay dimensionless.'}
            </p>
          </div>
        </div>
      </div>
      {disclaimer ? (
        <p className="mt-3 text-xs italic text-muted-foreground">{disclaimer}</p>
      ) : null}
    </div>
  )
}
