import React, { useState, useEffect, useRef, useCallback } from 'react';
import { state, subscribe, addModule, emit, addConnection, updateModulePosition, deserialize } from './engine/state.js';
import { startAudio, stopAudio, connectWire, rebuildGraph } from './engine/audioEngine.js';
import { getModuleDef } from './engine/moduleRegistry.js';
import { autoSave, autoLoad, exportPatch, importPatch } from './engine/presets.js';
import ModuleNode from './components/ModuleNode.jsx';
import WireLayer from './components/WireLayer.jsx';
import ModulePalette from './components/ModulePalette.jsx';
import PresetModal from './components/PresetModal.jsx';
import { CHIPTUNE_PRESET } from './presets/chiptune.js';

export default function App() {
  const [, forceUpdate] = useState(0);
  const containerRef = useRef(null);
  const portPositions = useRef({});
  const [tempWire, setTempWire] = useState(null);
  const connectingRef = useRef(null);
  const [presetModal, setPresetModal] = useState(null);
  const importRef = useRef(null);

  // Subscribe to state changes
  useEffect(() => {
    const unsub = subscribe(() => forceUpdate(n => n + 1));
    return unsub;
  }, []);

  // Auto-load on mount, or load chiptune demo if empty
  useEffect(() => {
    const loaded = autoLoad();
    if (!loaded || state.modules.length === 0) {
      // Load chiptune demo preset
      deserialize(CHIPTUNE_PRESET);
    }
  }, []);

  // Auto-save interval
  useEffect(() => {
    const interval = setInterval(autoSave, 3000);
    return () => clearInterval(interval);
  }, []);

  // Port position reporting
  const handlePortPosition = useCallback((moduleId, portName, direction, el) => {
    const key = `${moduleId}-${portName}-${direction}`;
    portPositions.current[key] = el;
  }, []);

  // Start connecting a wire
  const handleStartConnect = useCallback((info) => {
    connectingRef.current = info;
    const containerRect = containerRef.current.getBoundingClientRect();
    setTempWire({
      portType: info.portType,
      startX: info.startX - containerRect.left,
      startY: info.startY - containerRect.top,
      endX: info.startX - containerRect.left,
      endY: info.startY - containerRect.top,
    });
  }, []);

  // Find port-dot element at pointer position (including nearby)
  const findPortAtPoint = (x, y) => {
    // First try exact hit
    const el = document.elementFromPoint(x, y);
    if (el && el.classList.contains('port-dot') && el.dataset.moduleId) {
      return el;
    }
    // Try a small radius around the point
    for (const [dx, dy] of [[0,0],[-8,0],[8,0],[0,-8],[0,8],[-6,-6],[6,-6],[-6,6],[6,6]]) {
      const hit = document.elementFromPoint(x + dx, y + dy);
      if (hit && hit.classList.contains('port-dot') && hit.dataset.moduleId) {
        return hit;
      }
    }
    return null;
  };

  // Canvas pointer events
  const handlePointerDown = useCallback((e) => {
    if (e.button === 1 || (e.button === 0 && e.altKey) || e.button === 2) {
      state.panning = true;
      state.panStart = { x: e.clientX - state.camX, y: e.clientY - state.camY };
      e.preventDefault();
    } else if (e.button === 0 && !connectingRef.current) {
      state.selectedModuleId = null;
      emit();
    }
  }, []);

  const handlePointerMove = useCallback((e) => {
    if (state.panning && state.panStart) {
      state.camX = e.clientX - state.panStart.x;
      state.camY = e.clientY - state.panStart.y;
      emit();
      return;
    }
    if (state.dragging) {
      const newX = e.clientX / state.zoom - state.dragging.offsetX;
      const newY = e.clientY / state.zoom - state.dragging.offsetY;
      updateModulePosition(state.dragging.moduleId, newX, newY);
      return;
    }
    if (connectingRef.current && containerRef.current) {
      const containerRect = containerRef.current.getBoundingClientRect();
      setTempWire(prev => prev ? {
        ...prev,
        endX: e.clientX - containerRect.left,
        endY: e.clientY - containerRect.top,
      } : null);
    }
  }, []);

  const handlePointerUp = useCallback((e) => {
    if (state.panning) {
      state.panning = false;
      state.panStart = null;
    }
    if (state.dragging) {
      state.dragging = null;
      emit();
    }

    // End connecting
    if (connectingRef.current) {
      const portEl = findPortAtPoint(e.clientX, e.clientY);
      if (portEl) {
        finishConnection(portEl);
      }
      connectingRef.current = null;
      setTempWire(null);
    }
  }, []);

  const finishConnection = (portEl) => {
    const from = connectingRef.current;
    if (!from) return;

    // Read data attributes directly — clean and reliable
    const targetModuleId = parseInt(portEl.dataset.moduleId);
    const targetPort = portEl.dataset.portName;
    const targetDirection = portEl.dataset.portDirection;

    if (!targetModuleId || !targetPort || !targetDirection) return;
    if (targetModuleId === from.moduleId && targetPort === from.port) return;

    // Determine from/to
    let fromMod, fromPort, toMod, toPort;
    if (from.direction === 'output' && targetDirection === 'input') {
      fromMod = from.moduleId; fromPort = from.port;
      toMod = targetModuleId; toPort = targetPort;
    } else if (from.direction === 'input' && targetDirection === 'output') {
      fromMod = targetModuleId; fromPort = targetPort;
      toMod = from.moduleId; toPort = from.port;
    } else {
      return; // same direction — invalid
    }

    const connId = addConnection(fromMod, fromPort, toMod, toPort);
    if (connId && state.isRunning) {
      const conn = state.connections.find(c => c.id === connId);
      if (conn) connectWire(conn);
    }
  };

  const handleWheel = useCallback((e) => {
    e.preventDefault();
    const delta = -e.deltaY * 0.001;
    state.zoom = Math.max(0.3, Math.min(3, state.zoom + delta));
    emit();
  }, []);

  const handleContextMenu = useCallback((e) => e.preventDefault(), []);

  const handleToggleAudio = async () => {
    if (state.isRunning) {
      stopAudio();
    } else {
      await startAudio();
    }
    emit();
  };

  const handleAddModule = (type) => {
    const x = (-state.camX + 300) / state.zoom + Math.random() * 50;
    const y = (-state.camY + 200) / state.zoom + Math.random() * 50;
    addModule(type, x, y);
    if (state.isRunning) rebuildGraph();
  };

  const handleImport = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    await importPatch(file);
    emit();
    e.target.value = '';
  };

  const handleLoadDemo = () => {
    deserialize(CHIPTUNE_PRESET);
    if (state.isRunning) rebuildGraph();
    emit();
  };

  return (
    <div className="app">
      {/* Toolbar */}
      <div className="toolbar">
        <span className="toolbar-title">Reaktor</span>
        <div className="toolbar-sep" />
        <button className={`toolbar-btn ${state.isRunning ? 'active' : ''}`} onClick={handleToggleAudio}>
          {state.isRunning ? '⏹ Stop' : '▶ Start'}
        </button>
        <div className="toolbar-sep" />
        <div className="toolbar-group">
          <button className="toolbar-btn" onClick={() => setPresetModal('save')}>💾 Save</button>
          <button className="toolbar-btn" onClick={() => setPresetModal('load')}>📂 Load</button>
          <button className="toolbar-btn" onClick={exportPatch}>📤 Export</button>
          <button className="toolbar-btn" onClick={() => importRef.current?.click()}>📥 Import</button>
          <input ref={importRef} type="file" accept=".json" style={{ display: 'none' }} onChange={handleImport} />
        </div>
        <div className="toolbar-sep" />
        <button className="toolbar-btn" onClick={handleLoadDemo} style={{ color: 'var(--yellow)' }}>
          🎮 Chiptune Demo
        </button>
        <div className="toolbar-sep" />
        <span className="toolbar-label" style={{ color: state.isRunning ? 'var(--green)' : 'var(--text2)' }}>
          {state.isRunning ? '● LIVE' : '○ OFF'}
        </span>
        <span className="toolbar-label" style={{ marginLeft: 'auto' }}>
          {state.modules.length} modules · {state.connections.length} wires
        </span>
      </div>

      {/* Main canvas area */}
      <div className="main-area">
        <div
          ref={containerRef}
          className={`node-canvas ${state.panning ? 'grabbing' : ''} ${connectingRef.current ? 'connecting' : ''}`}
          onPointerDown={handlePointerDown}
          onPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onWheel={handleWheel}
          onContextMenu={handleContextMenu}
        >
          {/* Grid background */}
          <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', zIndex: 0 }}>
            <defs>
              <pattern id="grid" width={20 * state.zoom} height={20 * state.zoom}
                patternUnits="userSpaceOnUse"
                x={state.camX % (20 * state.zoom)} y={state.camY % (20 * state.zoom)}>
                <circle cx={1} cy={1} r={0.8} fill="#1a1a30" />
              </pattern>
            </defs>
            <rect width="100%" height="100%" fill="url(#grid)" />
          </svg>

          {/* Wire layer (behind modules, uses getBoundingClientRect) */}
          <WireLayer portPositions={portPositions} tempWire={tempWire} containerRef={containerRef} zoom={state.zoom} camX={state.camX} camY={state.camY} />

          {/* Modules container (offset by camera) */}
          <div style={{ position: 'absolute', left: state.camX, top: state.camY, zIndex: 2 }}>
            {state.modules.map(mod => (
              <ModuleNode
                key={mod.id}
                mod={mod}
                zoom={state.zoom}
                onStartConnect={handleStartConnect}
                onPortPosition={handlePortPosition}
              />
            ))}
          </div>
        </div>

        {/* Module palette */}
        <ModulePalette onAddModule={handleAddModule} />
      </div>

      {/* Status bar */}
      <div className="status-bar">
        <span className="status-accent">Reaktor — MontLab Modular Synth</span>
        <span>Zoom: {(state.zoom * 100).toFixed(0)}%</span>
        <span>RClick: pan · Wheel: zoom · Drag port: wire · Click wire: delete</span>
      </div>

      {presetModal && <PresetModal mode={presetModal} onClose={() => setPresetModal(null)} />}
    </div>
  );
}
