import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { SequencePanel } from './ui/SequencePanel.tsx'
import { PhantomPanel } from './ui/PhantomPanel.tsx'
import { KspaceView } from './kspace/KspaceView.tsx'
import { BUILTIN_PHANTOMS, fetchBuiltinPhantom } from './phantom/builtins.ts'
import type { Phantom } from './phantom/phantomTypes.ts'
import {
  addSequence,
  deleteSequence,
  listSequences,
  renameSequence,
} from './storage/seqStore.ts'
import type { StoredSequence } from './storage/seqStore.ts'
import { summarizeSeq } from './seq/summary.ts'
import { useSimulation } from './sim/useSimulation.ts'

function stripExt(name: string): string {
  return name.replace(/\.seq$/i, '')
}

export default function App() {
  const [sequences, setSequences] = useState<StoredSequence[]>([])
  const [selectedSeqId, setSelectedSeqId] = useState<string | null>(null)
  const [phantomId, setPhantomId] = useState<string>(BUILTIN_PHANTOMS[0].id)
  const [phantom, setPhantom] = useState<Phantom | null>(null)
  const [phantomLoading, setPhantomLoading] = useState(false)
  const [uploadError, setUploadError] = useState<string | null>(null)
  const phantomCache = useRef<Map<string, Phantom>>(new Map())

  const { state: sim, run, cancel, reset } = useSimulation()

  const refresh = useCallback(async () => {
    const list = await listSequences()
    setSequences(list)
    setSelectedSeqId((cur) => cur ?? (list.length ? list[0].id : null))
  }, [])

  useEffect(() => {
    void refresh()
  }, [refresh])

  // Load the selected phantom (cached).
  useEffect(() => {
    let cancelled = false
    const cached = phantomCache.current.get(phantomId)
    if (cached) {
      setPhantom(cached)
      return
    }
    const builtin = BUILTIN_PHANTOMS.find((b) => b.id === phantomId)
    if (!builtin) return
    setPhantomLoading(true)
    fetchBuiltinPhantom(builtin)
      .then((p) => {
        if (cancelled) return
        phantomCache.current.set(phantomId, p)
        setPhantom(p)
      })
      .catch((err) => {
        if (!cancelled) console.error('Failed to load phantom', err)
      })
      .finally(() => {
        if (!cancelled) setPhantomLoading(false)
      })
    return () => {
      cancelled = true
    }
  }, [phantomId])

  const selectedSeq = useMemo(
    () => sequences.find((s) => s.id === selectedSeqId) ?? null,
    [sequences, selectedSeqId],
  )
  const summary = useMemo(() => (selectedSeq ? summarizeSeq(selectedSeq.text) : null), [selectedSeq])

  const handleUpload = useCallback(
    async (files: FileList) => {
      setUploadError(null)
      let lastId: string | null = null
      for (const file of Array.from(files)) {
        const text = await file.text()
        const s = summarizeSeq(text)
        if (!s.ok) {
          setUploadError(`"${file.name}" is not a valid .seq file: ${s.error}`)
          continue
        }
        const rec = await addSequence(stripExt(file.name), text)
        lastId = rec.id
      }
      await refresh()
      if (lastId) setSelectedSeqId(lastId)
    },
    [refresh],
  )

  const handleRename = useCallback(
    async (id: string, name: string) => {
      await renameSequence(id, name)
      await refresh()
    },
    [refresh],
  )

  const handleDelete = useCallback(
    async (id: string) => {
      await deleteSequence(id)
      const list = await listSequences()
      setSequences(list)
      setSelectedSeqId((cur) => (cur === id ? (list[0]?.id ?? null) : cur))
    },
    [],
  )

  const canRun = !!selectedSeq && !!phantom && sim.status !== 'running'
  const handleRun = useCallback(() => {
    if (!selectedSeq || !phantom) return
    reset()
    run(selectedSeq.text, phantom)
  }, [selectedSeq, phantom, run, reset])

  const pct = sim.progress ? Math.round(sim.progress.fraction * 100) : 0

  return (
    <div className="app">
      <header className="app-header">
        <div className="brand">
          <span className="logo" aria-hidden>
            ⌾
          </span>
          <div>
            <h1>mri-scanner</h1>
            <p>Upload a pulseq sequence, run a Bloch simulation on a digital phantom, and view the raw k-space.</p>
          </div>
        </div>
        <a className="header-link" href="https://concept-collection.github.io/seqlab/" target="_blank" rel="noreferrer">
          seqlab ↗
        </a>
      </header>

      <div className="layout">
        <aside className="sidebar">
          <SequencePanel
            sequences={sequences}
            selectedId={selectedSeqId}
            onSelect={setSelectedSeqId}
            onUpload={handleUpload}
            onRename={handleRename}
            onDelete={handleDelete}
          />
          {uploadError && <div className="error-banner">{uploadError}</div>}

          <PhantomPanel
            builtins={BUILTIN_PHANTOMS}
            selectedId={phantomId}
            onSelect={setPhantomId}
            phantom={phantom}
            loading={phantomLoading}
          />

          <section className="panel run-panel">
            <div className="panel-head">
              <h2>Simulate</h2>
            </div>
            {summary && summary.ok && (
              <div className="seq-summary">
                <div>
                  <span className="k">Sequence</span>
                  <span className="v">{selectedSeq?.name}</span>
                </div>
                <div>
                  <span className="k">Duration</span>
                  <span className="v">{summary.durationSec.toFixed(3)} s</span>
                </div>
                <div>
                  <span className="k">Blocks / RF / ADC</span>
                  <span className="v">
                    {summary.numBlocks} / {summary.rfCount} / {summary.adcCount}
                  </span>
                </div>
                <div>
                  <span className="k">ADC samples</span>
                  <span className="v">{summary.adcSamples.toLocaleString()}</span>
                </div>
              </div>
            )}
            {!selectedSeq && <p className="muted">Select a sequence to simulate.</p>}

            <div className="run-controls">
              {sim.status === 'running' ? (
                <button type="button" className="btn danger" onClick={cancel}>
                  Cancel
                </button>
              ) : (
                <button type="button" className="btn primary" disabled={!canRun} onClick={handleRun}>
                  ▶ Run simulation
                </button>
              )}
            </div>

            {sim.status === 'running' && (
              <div className="progress">
                <div className="progress-bar">
                  <div className="progress-fill" style={{ width: `${pct}%` }} />
                </div>
                <div className="progress-text">
                  {pct}% · {sim.progress?.samplesDone.toLocaleString()}/
                  {sim.progress?.numSamples.toLocaleString()} samples · {(sim.elapsedMs / 1000).toFixed(1)} s
                </div>
              </div>
            )}
            {sim.status === 'cancelled' && <p className="muted">Simulation cancelled.</p>}
            {sim.status === 'error' && <div className="error-banner">Simulation error: {sim.error}</div>}
          </section>
        </aside>

        <main className="main">
          {sim.status === 'done' && sim.result ? (
            <KspaceView signal={sim.result} elapsedMs={sim.elapsedMs} />
          ) : (
            <div className="placeholder">
              <div className="placeholder-inner">
                {sim.status === 'running' ? (
                  <>
                    <div className="spinner" />
                    <p>Simulating {selectedSeq?.name}…</p>
                  </>
                ) : (
                  <>
                    <span className="placeholder-icon" aria-hidden>
                      ⌾
                    </span>
                    <p>The raw k-space will appear here after you run a simulation.</p>
                  </>
                )}
              </div>
            </div>
          )}
        </main>
      </div>
    </div>
  )
}
