import React, { useState, useEffect, useCallback, useRef } from 'react';
import {
  Zap, Trash2, RefreshCw, Waves,
  ShieldAlert, Binary, BrainCircuit, Dna
} from 'lucide-react';
import GridCell from './components/GridCell';

const GRID_SIZE = 8;
const CELL_COUNT = GRID_SIZE * GRID_SIZE;
const MAX_HISTORY = 50;

// Pre-allocate empty arrays to avoid recreating on every clear
const EMPTY_BOOLS = Object.freeze(Array(CELL_COUNT).fill(false));
const EMPTY_WEIGHTS = Object.freeze(Array(CELL_COUNT).fill(0));
const FULL_HISTORY = Object.freeze(Array(MAX_HISTORY).fill(100));

const App = () => {
  // ── Render state (drives UI) ──
  const [signalGrid, setSignalGrid] = useState([...EMPTY_BOOLS]);
  const [memoryGrid, setMemoryGrid] = useState([...EMPTY_BOOLS]);
  const [weights, setWeights] = useState([...EMPTY_WEIGHTS]);
  const [accuracy, setAccuracy] = useState(100);
  const [history, setHistory] = useState([...FULL_HISTORY]);
  const [glitch, setGlitch] = useState(false);
  const [isDrifting, setIsDrifting] = useState(false);

  // Slider state (UI display)
  const [strength, setStrength] = useState(85);
  const [frequency, setFrequency] = useState(50);
  const [persistence, setPersistence] = useState(30);
  const [crossTalk, setCrossTalk] = useState(20);
  const [plasticity, setPlasticity] = useState(40);

  // ── Refs (simulation reads from these — no dependency churn) ──
  const signalRef = useRef(signalGrid);
  const memoryRef = useRef(memoryGrid);
  const weightsRef = useRef(weights);
  const strengthRef = useRef(strength);
  const frequencyRef = useRef(frequency);
  const persistenceRef = useRef(persistence);
  const crossTalkRef = useRef(crossTalk);
  const plasticityRef = useRef(plasticity);
  const isDriftingRef = useRef(isDrifting);

  const simTimerRef = useRef(null);
  const driftTimerRef = useRef(null);
  const waveCanvasRef = useRef(null);
  const waveFrameRef = useRef(null);
  const waveFrameCount = useRef(0);
  const accuracyRef = useRef(100);
  const containerRef = useRef(null);

  // Keep refs in sync with state
  useEffect(() => { signalRef.current = signalGrid; }, [signalGrid]);
  useEffect(() => { strengthRef.current = strength; }, [strength]);
  useEffect(() => { frequencyRef.current = frequency; }, [frequency]);
  useEffect(() => { persistenceRef.current = persistence; }, [persistence]);
  useEffect(() => { crossTalkRef.current = crossTalk; }, [crossTalk]);
  useEffect(() => { plasticityRef.current = plasticity; }, [plasticity]);
  useEffect(() => { isDriftingRef.current = isDrifting; }, [isDrifting]);

  // ── Actions ──
  const generatePattern = useCallback(() => {
    const newGrid = Array.from({ length: CELL_COUNT }, () => Math.random() > 0.8);
    setSignalGrid(newGrid);
    signalRef.current = newGrid;
    setGlitch(true);
    setTimeout(() => setGlitch(false), 150);
  }, []);

  const clearGrids = useCallback(() => {
    const s = [...EMPTY_BOOLS];
    const m = [...EMPTY_BOOLS];
    const w = [...EMPTY_WEIGHTS];
    setSignalGrid(s);
    setMemoryGrid(m);
    setWeights(w);
    setHistory([...FULL_HISTORY]);
    signalRef.current = s;
    memoryRef.current = m;
    weightsRef.current = w;
    accuracyRef.current = 100;
  }, []);

  const toggleBlock = useCallback((index) => {
    setSignalGrid(prev => {
      const next = [...prev];
      next[index] = !next[index];
      signalRef.current = next;
      return next;
    });
  }, []);

  // ── Signal Drift ──
  useEffect(() => {
    const runDrift = () => {
      if (!isDriftingRef.current) return;
      setSignalGrid(prev => {
        const next = [...prev];
        const last = next.pop();
        next.unshift(last);
        signalRef.current = next;
        return next;
      });
    };

    // Restart drift interval when frequency or drift toggle changes
    clearInterval(driftTimerRef.current);
    if (isDrifting) {
      driftTimerRef.current = setInterval(runDrift, 2000 - frequency * 15);
    }
    return () => clearInterval(driftTimerRef.current);
  }, [isDrifting, frequency]);

  // ── Main Simulation Loop (runs once on mount) ──
  useEffect(() => {
    const tick = () => {
      const signal = signalRef.current;
      const prevMemory = memoryRef.current;
      const prevWeights = weightsRef.current;
      const str = strengthRef.current;
      const freq = frequencyRef.current;
      const pers = persistenceRef.current;
      const ct = crossTalkRef.current;
      const plast = plasticityRef.current;

      const baseSnrThreshold = (100 - str) / 100;

      // Build next memory
      const nextGrid = new Array(CELL_COUNT);
      for (let i = 0; i < CELL_COUNT; i++) {
        const noise = Math.random();
        const shouldUpdate = Math.random() < freq / 100;

        if (!shouldUpdate) {
          nextGrid[i] = prevMemory[i];
          continue;
        }

        const localWeight = prevWeights[i] * (plast / 100);
        const effectiveThreshold = Math.max(0.01, baseSnrThreshold - localWeight);

        if (signal[i]) {
          nextGrid[i] = noise > effectiveThreshold;
        } else if (prevMemory[i] && Math.random() < pers / 100) {
          nextGrid[i] = true;
        } else {
          nextGrid[i] = noise < effectiveThreshold / 20;
        }
      }

      // Spatial cross-talk
      let finalGrid = nextGrid;
      if (ct > 0) {
        finalGrid = new Array(CELL_COUNT);
        for (let i = 0; i < CELL_COUNT; i++) {
          if (nextGrid[i]) {
            finalGrid[i] = true;
            continue;
          }
          const row = (i / GRID_SIZE) | 0;
          const col = i % GRID_SIZE;
          let activeNeighbors = 0;
          for (let r = -1; r <= 1; r++) {
            for (let c = -1; c <= 1; c++) {
              if (r === 0 && c === 0) continue;
              const nr = row + r, nc = col + c;
              if (nr >= 0 && nr < GRID_SIZE && nc >= 0 && nc < GRID_SIZE) {
                if (nextGrid[nr * GRID_SIZE + nc]) activeNeighbors++;
              }
            }
          }
          finalGrid[i] = Math.random() < activeNeighbors * (ct / 1000);
        }
      }

      // Hebbian weight update
      const nextWeights = new Array(CELL_COUNT);
      for (let i = 0; i < CELL_COUNT; i++) {
        if (signal[i] && finalGrid[i]) {
          nextWeights[i] = Math.min(1, prevWeights[i] + 0.02);
        } else if (!signal[i] && !finalGrid[i]) {
          nextWeights[i] = prevWeights[i];
        } else {
          nextWeights[i] = Math.max(0, prevWeights[i] - 0.01);
        }
      }

      // Accuracy
      let matches = 0;
      for (let i = 0; i < CELL_COUNT; i++) {
        if (signal[i] === finalGrid[i]) matches++;
      }
      const acc = Math.round((matches / CELL_COUNT) * 100);

      // Commit to refs
      memoryRef.current = finalGrid;
      weightsRef.current = nextWeights;
      accuracyRef.current = acc;

      // Batch state updates
      setMemoryGrid(finalGrid);
      setWeights(nextWeights);
      setAccuracy(acc);
      setHistory(prev => {
        const next = [...prev];
        next.shift();
        next.push(acc);
        return next;
      });
    };

    // Adaptive tick rate — re-read frequency ref each cycle
    const scheduleNext = () => {
      const intervalTime = Math.max(16, 1000 - frequencyRef.current * 9.8);
      simTimerRef.current = setTimeout(() => {
        tick();
        scheduleNext();
      }, intervalTime);
    };

    scheduleNext();
    return () => clearTimeout(simTimerRef.current);
  }, []); // mount-only — reads everything from refs

  // ── Waveform Canvas (ResizeObserver + proper rAF cleanup) ──
  useEffect(() => {
    const canvas = waveCanvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const container = canvas.parentElement;

    // Sync canvas resolution to container
    const resizeCanvas = () => {
      const rect = container.getBoundingClientRect();
      const dpr = window.devicePixelRatio || 1;
      canvas.width = rect.width * dpr;
      canvas.height = rect.height * dpr;
      ctx.scale(dpr, dpr);
      canvas.style.width = rect.width + 'px';
      canvas.style.height = rect.height + 'px';
    };

    const ro = new ResizeObserver(resizeCanvas);
    ro.observe(container);
    resizeCanvas();

    const animate = () => {
      waveFrameCount.current++;
      const frame = waveFrameCount.current;
      const w = canvas.width / (window.devicePixelRatio || 1);
      const h = canvas.height / (window.devicePixelRatio || 1);

      ctx.clearRect(0, 0, w, h);
      ctx.beginPath();
      ctx.strokeStyle = accuracyRef.current < 50 ? '#ff0055' : '#00ff9f';
      ctx.lineWidth = 2;

      const freq = frequencyRef.current;
      const str = strengthRef.current;
      const freqMod = freq / 20;
      const ampMod = str / 4;
      const noiseMod = (100 - str) / 5;

      for (let x = 0; x < w; x++) {
        const y = h / 2 +
          Math.sin(x * 0.05 * freqMod + frame * 0.1) * ampMod +
          Math.sin(x * 0.1 + frame * 0.2) * noiseMod;
        if (x === 0) ctx.moveTo(x, y);
        else ctx.lineTo(x, y);
      }
      ctx.stroke();

      waveFrameRef.current = requestAnimationFrame(animate);
    };

    waveFrameRef.current = requestAnimationFrame(animate);

    return () => {
      cancelAnimationFrame(waveFrameRef.current);
      ro.disconnect();
    };
  }, []);

  // ── Slider helpers ──
  const sliderHandler = useCallback((setter, ref) => (e) => {
    const v = parseInt(e.target.value);
    setter(v);
    ref.current = v;
  }, []);

  return (
    <div className={`min-h-screen p-4 md:p-6 font-mono selection:bg-[#00ff9f] selection:text-black transition-colors duration-500 ${accuracy < 25 ? 'bg-[#200] text-[#ff0055]' : 'bg-[#020202] text-[#00ff9f]'}`}>
      {/* Scanline overlay */}
      <div className="fixed inset-0 pointer-events-none z-50 opacity-[0.05] bg-[linear-gradient(rgba(18,16,16,0)_50%,rgba(0,0,0,0.25)_50%),linear-gradient(90deg,rgba(255,0,0,0.06),rgba(0,255,0,0.02),rgba(0,0,255,0.06))] bg-[length:100%_2px,3px_100%]" />

      <div className="max-w-7xl mx-auto space-y-6">
        {/* Header */}
        <header className="flex flex-col md:flex-row items-start md:items-center justify-between p-6 bg-[#0a0a0a] border-2 border-[#00ff9f]/20 rounded-sm relative overflow-hidden">
          <div className="z-10 flex items-center gap-5">
            <div className={`p-3 rounded-none shadow-lg transition-colors ${accuracy < 25 ? 'bg-[#ff0055] shadow-[#ff0055]' : 'bg-[#00ff9f] shadow-[#00ff9f]'}`}>
              <BrainCircuit size={32} className="text-black" />
            </div>
            <div>
              <h1 className={`text-4xl font-black uppercase tracking-tighter ${glitch || accuracy < 25 ? 'animate-pulse' : ''}`}>
                Synapse_Link <span className="text-white text-2xl font-light opacity-50">HEBB-9</span>
              </h1>
              <div className="flex items-center gap-3 text-[10px] mt-1 font-bold opacity-60 uppercase">
                <span className="flex items-center gap-1"><Dna size={12} /> PLASTICITY: ACTIVE</span>
                <span className={`flex items-center gap-1 ${accuracy < 40 ? 'text-[#ff0055] animate-bounce' : ''}`}>
                  <ShieldAlert size={12} /> {accuracy < 25 ? 'CRITICAL DECOHERENCE' : 'SIGNAL STABLE'}
                </span>
              </div>
            </div>
          </div>

          <div className="mt-4 md:mt-0 flex flex-wrap gap-3">
            <button
              onClick={() => setIsDrifting(d => !d)}
              className={`px-6 py-3 font-black uppercase tracking-widest text-xs transition-all flex items-center gap-2 ${isDrifting ? 'bg-white text-black' : 'border border-[#00ff9f] text-[#00ff9f]'}`}
            >
              <Waves size={16} /> {isDrifting ? 'Stop Drift' : 'Enable Drift'}
            </button>
            <button onClick={generatePattern} className="bg-[#00ff9f] text-black px-6 py-3 font-black uppercase tracking-widest text-xs hover:bg-white transition-all shadow-md flex items-center gap-2">
              <RefreshCw size={16} /> Sync Source
            </button>
            <button onClick={clearGrids} className="border border-[#ff0055] text-[#ff0055] px-6 py-3 font-black uppercase tracking-widest text-xs hover:bg-[#ff0055] hover:text-black transition-all flex items-center gap-2">
              <Trash2 size={16} /> Purge
            </button>
          </div>
        </header>

        {/* Waveform + Fidelity */}
        <div className="grid lg:grid-cols-4 gap-6">
          <div className="lg:col-span-3 bg-[#0a0a0a] border border-[#00ff9f]/20 p-4 relative h-32" ref={containerRef}>
            <div className="absolute top-2 left-2 text-[8px] opacity-40 uppercase font-black">Bio_Feedback_Wave</div>
            <canvas ref={waveCanvasRef} className="w-full h-full" />
          </div>
          <div className={`bg-[#0a0a0a] border border-[#00ff9f]/20 p-4 flex flex-col justify-center items-center ${accuracy < 25 ? 'border-[#ff0055]' : ''}`}>
            <div className="text-[10px] uppercase opacity-40 font-black mb-1">Engram Fidelity</div>
            <div className={`text-4xl font-black italic tracking-tighter ${accuracy < 60 ? 'text-[#ff0055]' : 'text-white'}`}>
              {accuracy}.0
            </div>
            <div className="w-full bg-[#111] h-1 mt-2">
              <div className="bg-[#00ff9f] h-full transition-all" style={{ width: `${accuracy}%` }} />
            </div>
          </div>
        </div>

        {/* Grids */}
        <div className="grid lg:grid-cols-2 gap-8">
          {/* Signal Input */}
          <div className="bg-[#0a0a0a] border-l-4 border-[#00ff9f] p-6 relative">
            <div className="flex items-center justify-between mb-6">
              <h3 className="font-black uppercase tracking-widest text-white flex items-center gap-2">
                <Zap size={18} className="text-[#00ff9f]" /> Stimulus Source
              </h3>
              <div className="text-[10px] opacity-40 font-bold">MANUAL_OVERRIDE_ENABLED</div>
            </div>
            <div className="grid grid-cols-8 gap-2 aspect-square bg-black/40 p-3 border border-[#00ff9f]/10" role="grid" aria-label="Stimulus grid">
              {signalGrid.map((active, i) => (
                <GridCell key={i} active={active} variant="signal" weight={0} mismatch={false} onClick={() => toggleBlock(i)} />
              ))}
            </div>
          </div>

          {/* Memory Output */}
          <div className="bg-[#0a0a0a] border-r-4 border-[#ff0055] p-6 relative">
            <div className="flex items-center justify-between mb-6">
              <h3 className="font-black uppercase tracking-widest text-white flex items-center gap-2">
                <BrainCircuit size={18} className="text-[#ff0055]" /> Synaptic Cache
              </h3>
              <div className="flex items-center gap-2 text-[8px] font-bold">
                <div className="w-2 h-2 bg-white rounded-full animate-ping" />
                LEARNING_ACTIVE
              </div>
            </div>
            <div className="grid grid-cols-8 gap-2 aspect-square bg-black/40 p-3 border border-[#ff0055]/10" role="grid" aria-label="Synaptic memory grid">
              {memoryGrid.map((active, i) => (
                <GridCell key={i} active={active} variant="memory" weight={weights[i]} mismatch={signalGrid[i] !== active} />
              ))}
            </div>
          </div>
        </div>

        {/* Sliders */}
        <div className="grid md:grid-cols-3 lg:grid-cols-5 gap-4">
          <div className="bg-[#0a0a0a] p-4 border border-[#00ff9f]/20">
            <label className="flex justify-between text-[10px] font-black uppercase mb-3">
              <span>SNR_Amp</span><span>{strength}%</span>
            </label>
            <input type="range" min="0" max="100" value={strength} onChange={sliderHandler(setStrength, strengthRef)} className="w-full accent-[#00ff9f]" />
          </div>
          <div className="bg-[#0a0a0a] p-4 border border-[#00ff9f]/20">
            <label className="flex justify-between text-[10px] font-black uppercase mb-3">
              <span>Refresh_Hz</span><span>{frequency}%</span>
            </label>
            <input type="range" min="1" max="100" value={frequency} onChange={sliderHandler(setFrequency, frequencyRef)} className="w-full accent-[#00ff9f]" />
          </div>
          <div className="bg-[#0a0a0a] p-4 border border-[#ff0055]/20">
            <label className="flex justify-between text-[10px] font-black uppercase mb-3 text-[#ff0055]">
              <span>Persistence</span><span>{persistence}%</span>
            </label>
            <input type="range" min="0" max="100" value={persistence} onChange={sliderHandler(setPersistence, persistenceRef)} className="w-full accent-[#ff0055]" />
          </div>
          <div className="bg-[#0a0a0a] p-4 border border-[#ff0055]/20">
            <label className="flex justify-between text-[10px] font-black uppercase mb-3 text-[#ff0055]">
              <span>Cross_Talk</span><span>{crossTalk}%</span>
            </label>
            <input type="range" min="0" max="100" value={crossTalk} onChange={sliderHandler(setCrossTalk, crossTalkRef)} className="w-full accent-[#ff0055]" />
          </div>
          <div className="bg-[#111] p-4 border border-white/20">
            <label className="flex justify-between text-[10px] font-black uppercase mb-3 text-white">
              <span>Plasticity</span><span>{plasticity}%</span>
            </label>
            <input type="range" min="0" max="100" value={plasticity} onChange={sliderHandler(setPlasticity, plasticityRef)} className="w-full accent-white" />
          </div>
        </div>

        {/* Telemetry Graph */}
        <div className="bg-[#0a0a0a] border border-[#00ff9f]/10 p-4 h-20 flex items-end gap-1 relative overflow-hidden" aria-label="Fidelity history">
          <div className="absolute inset-0 bg-gradient-to-t from-[#ff0055]/10 to-transparent pointer-events-none" />
          {history.map((val, i) => (
            <div key={i} className={`flex-1 transition-all ${val < 50 ? 'bg-[#ff0055]' : 'bg-[#00ff9f]/40'}`} style={{ height: `${val}%` }} />
          ))}
        </div>

        {/* Footer */}
        <footer className="p-4 bg-[#0a0a0a] border border-white/5 text-[9px] uppercase tracking-tighter opacity-60 grid grid-cols-2 gap-4">
          <div className="flex gap-2"><Binary size={12} /> DATA_PAGING: STOCHASTIC_GRADIENT_ENABLED</div>
          <div className="text-right">SYNAPTIC_WEIGHTS_NORMALIZED // HEBBIAN_COEFFICIENT: {(plasticity / 100).toFixed(2)}</div>
        </footer>
      </div>
    </div>
  );
};

export default App;
