// Main app — state, drag/drop, connections, persistence

const { useState, useEffect, useRef, useCallback, useMemo } = React;

const STORAGE_KEY = 'wattage_v1';

const defaultState = {
  sources: [],
  consumers: [],
  connections: [], // { id, sourceId, consumerId }
  period: 'monthly'
};

const sampleState = {
  sources: [
    { id: 's1', name: 'Day Job', amount: 5200, x: 80, y: 120 },
    { id: 's2', name: 'Freelance', amount: 1400, x: 80, y: 420 }
  ],
  consumers: [
    { id: 'c1', name: 'Mortgage', amount: 1850, x: 520, y: 110, icon: 'M' },
    { id: 'c2', name: 'Groceries', amount: 600, x: 520, y: 280, icon: 'G' },
    { id: 'c3', name: 'Car Payment', amount: 420, x: 520, y: 440, icon: 'C' },
    { id: 'c4', name: 'Utilities', amount: 240, x: 880, y: 110, icon: 'U' },
    { id: 'c5', name: 'Streaming', amount: 65, x: 880, y: 280, icon: 'S' },
    { id: 'c6', name: 'Vacation Fund', amount: 500, x: 880, y: 440, icon: 'V' }
  ],
  connections: [
    { id: 'k1', sourceId: 's1', consumerId: 'c1' },
    { id: 'k2', sourceId: 's1', consumerId: 'c2' },
    { id: 'k3', sourceId: 's1', consumerId: 'c3' },
    { id: 'k4', sourceId: 's1', consumerId: 'c4' },
    { id: 'k5', sourceId: 's2', consumerId: 'c5' },
    { id: 'k6', sourceId: 's2', consumerId: 'c6' }
  ],
  period: 'monthly'
};

const newId = (p) => p + '_' + Math.random().toString(36).slice(2, 9);

// Compute power flow allocations.
// For each consumer, divide its draw equally among its connected sources (cap at source remaining).
// Actually we use a simple fair model: each consumer's amount is split equally among its active sources;
// this models "shared load". When a source is overdrawn, its supplied stays at amount (capped).
function computeAllocations(sources, consumers, connections) {
  // Map source -> list of consumer connections
  const consumerSources = {}; // consumerId -> [sourceId]
  const sourceConsumers = {}; // sourceId -> [consumerId]
  for (const c of consumers) consumerSources[c.id] = [];
  for (const s of sources) sourceConsumers[s.id] = [];
  for (const k of connections) {
    if (consumerSources[k.consumerId]) consumerSources[k.consumerId].push(k.sourceId);
    if (sourceConsumers[k.sourceId]) sourceConsumers[k.sourceId].push(k.consumerId);
  }

  // For each connection, intended draw = consumer.amount / (number of source connections for this consumer)
  // This is the requested load on each source from this consumer.
  const requestedByConn = {};
  for (const k of connections) {
    const cons = consumers.find(c => c.id === k.consumerId);
    const srcCount = consumerSources[k.consumerId]?.length || 1;
    requestedByConn[k.id] = cons ? cons.amount / srcCount : 0;
  }

  // Total requested per source
  const sourceRequested = {};
  for (const s of sources) sourceRequested[s.id] = 0;
  for (const k of connections) sourceRequested[k.sourceId] += requestedByConn[k.id] || 0;

  // Source draw = min(requested, capacity) per connection (proportional if overdrawn)
  const allocPerConn = {};
  const sourceDraw = {};
  for (const s of sources) {
    const requested = sourceRequested[s.id];
    sourceDraw[s.id] = requested; // show the true requested draw (so overload visible)
    if (requested <= s.amount || requested === 0) {
      // No overload: each connection gets its full request
      for (const k of connections.filter(x => x.sourceId === s.id)) {
        allocPerConn[k.id] = requestedByConn[k.id];
      }
    } else {
      // Overload: scale down proportionally — total supply from this source = s.amount
      const scale = s.amount / requested;
      for (const k of connections.filter(x => x.sourceId === s.id)) {
        allocPerConn[k.id] = (requestedByConn[k.id] || 0) * scale;
      }
    }
  }

  // Consumer supplied = sum of allocPerConn for its connections
  const consumerSupply = {};
  for (const c of consumers) consumerSupply[c.id] = 0;
  for (const k of connections) {
    consumerSupply[k.consumerId] += allocPerConn[k.id] || 0;
  }

  return {
    ...allocPerConn,
    __sourceDraw: sourceDraw,
    __consumerSupply: consumerSupply
  };
}

const App = () => {
  const [state, setState] = useState(() => {
    try {
      const stored = localStorage.getItem(STORAGE_KEY);
      if (stored) return { ...defaultState, ...JSON.parse(stored) };
    } catch (e) {}
    return sampleState;
  });

  // Persist
  useEffect(() => {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch (e) {}
  }, [state]);

  const { sources, consumers, connections, period } = state;

  // Mobile detection
  const [isMobile, setIsMobile] = useState(() => typeof window !== 'undefined' && window.innerWidth < 760);
  useEffect(() => {
    const onResize = () => setIsMobile(window.innerWidth < 760);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  // Auto-arrange cards into a stacked column layout on mobile.
  // We compute display positions from the stored x/y, but on mobile we override.
  const layout = useMemo(() => {
    if (!isMobile) {
      const map = {};
      sources.forEach(s => { map[s.id] = { x: s.x, y: s.y }; });
      consumers.forEach(c => { map[c.id] = { x: c.x, y: c.y }; });
      return map;
    }
    // Mobile: scale down + auto-stack. Sources column on left, consumers on right.
    const w = window.innerWidth;
    const colSrcX = 8;
    const colConX = Math.max(150, w - 160 - 8); // 160 is consumer width on mobile
    const map = {};
    const topPad = 110;
    const srcGapY = 175;
    const conGapY = 150;
    sources.forEach((s, i) => { map[s.id] = { x: colSrcX, y: topPad + i * srcGapY }; });
    consumers.forEach((c, i) => { map[c.id] = { x: colConX, y: topPad + i * conGapY }; });
    return map;
  }, [isMobile, sources, consumers]);

  // Cable drag state
  const [pendingCable, setPendingCable] = useState(null); // { sourceId }
  const [mousePos, setMousePos] = useState(null);
  const [hoveredConsumer, setHoveredConsumer] = useState(null);

  // Card drag state
  const [dragging, setDragging] = useState(null); // { id, kind, offsetX, offsetY }

  const canvasRef = useRef(null);

  const allocations = useMemo(
    () => computeAllocations(sources, consumers, connections),
    [sources, consumers, connections]
  );

  // ======================= ACTIONS =======================
  const updateSource = (id, patch) => setState(s => ({
    ...s,
    sources: s.sources.map(x => x.id === id ? { ...x, ...patch } : x)
  }));
  const updateConsumer = (id, patch) => setState(s => ({
    ...s,
    consumers: s.consumers.map(x => x.id === id ? { ...x, ...patch } : x)
  }));
  const deleteSource = (id) => setState(s => ({
    ...s,
    sources: s.sources.filter(x => x.id !== id),
    connections: s.connections.filter(k => k.sourceId !== id)
  }));
  const deleteConsumer = (id) => setState(s => ({
    ...s,
    consumers: s.consumers.filter(x => x.id !== id),
    connections: s.connections.filter(k => k.consumerId !== id)
  }));
  const addSource = () => {
    const id = newId('s');
    // place in left column, stacked
    const baseY = 120 + sources.length * 220;
    setState(s => ({
      ...s,
      sources: [...s.sources, { id, name: 'Income Source', amount: 1000, x: 80, y: baseY }]
    }));
  };
  const addConsumer = () => {
    const id = newId('c');
    const col = consumers.length % 2;
    const row = Math.floor(consumers.length / 2);
    setState(s => ({
      ...s,
      consumers: [...s.consumers, {
        id, name: 'Expense', amount: 200,
        x: 520 + col * 360,
        y: 110 + row * 170,
        icon: 'E'
      }]
    }));
  };
  const removeConnection = (connId) => setState(s => ({
    ...s,
    connections: s.connections.filter(k => k.id !== connId)
  }));
  const addConnection = (sourceId, consumerId) => {
    if (connections.find(k => k.sourceId === sourceId && k.consumerId === consumerId)) return;
    setState(s => ({
      ...s,
      connections: [...s.connections, { id: newId('k'), sourceId, consumerId }]
    }));
  };
  const setPeriod = (p) => setState(s => ({ ...s, period: p }));
  const reset = () => {
    if (!confirm('Clear all sources, consumers and connections?')) return;
    setState(defaultState);
  };
  const loadSample = () => setState(sampleState);

  // ======================= POINTER EVENTS (mouse + touch) =======================

  // Helper: extract clientX/Y from mouse OR touch event
  const getPoint = (e) => {
    if (e.touches && e.touches.length) return { x: e.touches[0].clientX, y: e.touches[0].clientY };
    if (e.changedTouches && e.changedTouches.length) return { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY };
    return { x: e.clientX, y: e.clientY };
  };

  // Card drag
  const handleCardMouseDown = useCallback((e, id, kind) => {
    if (e.button !== undefined && e.button !== 0) return;
    if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;
    // On mobile, cards are auto-laid-out — disable card drag (port drag still works)
    if (typeof window !== 'undefined' && window.innerWidth < 760) return;
    const pt = getPoint(e);
    const rect = e.currentTarget.getBoundingClientRect();
    setDragging({
      id,
      kind,
      offsetX: pt.x - rect.left,
      offsetY: pt.y - rect.top
    });
    if (e.cancelable) e.preventDefault();
  }, []);

  // Port drag start
  const handlePortMouseDown = useCallback((e, sourceId) => {
    if (e.button !== undefined && e.button !== 0) return;
    const pt = getPoint(e);
    setPendingCable({ sourceId });
    setMousePos(pt);
    if (e.cancelable) e.preventDefault();
  }, []);

  useEffect(() => {
    const onMove = (e) => {
      const pt = getPoint(e);
      const x = pt.x;
      const y = pt.y;

      if (dragging) {
        const newX = x - dragging.offsetX;
        const newY = y - dragging.offsetY;
        if (dragging.kind === 'source') updateSource(dragging.id, { x: newX, y: newY });
        else updateConsumer(dragging.id, { x: newX, y: newY });
        if (e.cancelable) e.preventDefault();
      }

      if (pendingCable) {
        setMousePos({ x, y });
        const el = document.elementFromPoint(x, y);
        const card = el?.closest?.('[data-consumer-id]');
        if (card) setHoveredConsumer(card.getAttribute('data-consumer-id'));
        else setHoveredConsumer(null);
        if (e.cancelable) e.preventDefault();
      }
    };
    const onUp = (e) => {
      if (pendingCable) {
        if (hoveredConsumer) {
          addConnection(pendingCable.sourceId, hoveredConsumer);
        }
        setPendingCable(null);
        setMousePos(null);
        setHoveredConsumer(null);
      }
      if (dragging) setDragging(null);
    };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    window.addEventListener('touchmove', onMove, { passive: false });
    window.addEventListener('touchend', onUp);
    window.addEventListener('touchcancel', onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
      window.removeEventListener('touchmove', onMove);
      window.removeEventListener('touchend', onUp);
      window.removeEventListener('touchcancel', onUp);
    };
  }, [dragging, pendingCable, hoveredConsumer]);

  // Totals
  const totalIncome = sources.reduce((a, s) => a + (s.amount || 0), 0);
  const totalCommitted = consumers.reduce((a, c) => a + (c.amount || 0), 0);
  const totalSupplied = consumers.reduce((a, c) => a + (allocations.__consumerSupply[c.id] || 0), 0);

  return (
    <div ref={canvasRef} style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>

      <Toolbar
        period={period}
        onPeriodChange={setPeriod}
        onAddSource={addSource}
        onAddConsumer={addConsumer}
        onReset={reset}
        onLoadSample={loadSample}
      />

      {/* Cables BEHIND cards */}
      <CableLayer
        sources={sources.map(s => ({ ...s, ...layout[s.id] }))}
        consumers={consumers.map(c => ({ ...c, ...layout[c.id] }))}
        connections={connections}
        allocations={allocations}
        pendingCable={pendingCable}
        mousePos={mousePos}
        onCableClick={removeConnection}
        period={period}
        isMobile={isMobile}
      />

      {/* Sources */}
      {sources.map(s => (
        <Battery
          key={s.id}
          source={{ ...s, ...layout[s.id] }}
          draw={allocations.__sourceDraw[s.id] || 0}
          onChange={updateSource}
          onDelete={deleteSource}
          onPortMouseDown={handlePortMouseDown}
          onCardMouseDown={handleCardMouseDown}
          period={period}
          pendingTarget={pendingCable && pendingCable.sourceId === s.id}
          selected={dragging?.id === s.id}
          isMobile={isMobile}
        />
      ))}

      {/* Consumers */}
      {consumers.map(c => (
        <Consumer
          key={c.id}
          consumer={{ ...c, ...layout[c.id] }}
          supplied={allocations.__consumerSupply[c.id] || 0}
          onChange={updateConsumer}
          onDelete={deleteConsumer}
          onCardMouseDown={handleCardMouseDown}
          selected={dragging?.id === c.id}
          period={period}
          hoveredAsTarget={pendingCable && hoveredConsumer === c.id}
          isMobile={isMobile}
        />
      ))}

      {/* Empty state */}
      {sources.length === 0 && consumers.length === 0 && (
        <div style={{
          position: 'absolute',
          top: '50%', left: '50%',
          transform: 'translate(-50%, -50%)',
          textAlign: 'center',
          color: 'var(--ink-2)'
        }}>
          <div style={{
            fontFamily: 'JetBrains Mono, monospace',
            fontSize: 11,
            letterSpacing: '0.3em',
            color: 'var(--ink-3)',
            marginBottom: 12
          }}>// GRID OFFLINE</div>
          <div style={{ fontSize: 18, fontWeight: 600, color: 'var(--ink-1)', marginBottom: 8 }}>
            Add a power source to begin
          </div>
          <div style={{ fontSize: 13, color: 'var(--ink-2)', maxWidth: 360 }}>
            Drop in your income, plug in your expenses, and watch the grid balance itself.
          </div>
          <button
            onClick={loadSample}
            style={{
              marginTop: 16,
              background: 'linear-gradient(180deg, rgba(76, 243, 255, 0.15), rgba(76, 243, 255, 0.05))',
              border: '1px solid rgba(76, 243, 255, 0.3)',
              color: 'var(--cyan)',
              padding: '10px 18px',
              borderRadius: 8,
              fontSize: 12,
              fontWeight: 600,
              fontFamily: 'JetBrains Mono, monospace',
              letterSpacing: '0.1em'
            }}
          >
            LOAD SAMPLE GRID
          </button>
        </div>
      )}

      {/* Summary */}
      {(sources.length > 0 || consumers.length > 0) && (
        <SummaryPanel
          totalIncome={totalIncome}
          totalCommitted={totalCommitted}
          totalSupplied={totalSupplied}
          period={period}
          sources={sources}
          consumers={consumers}
        />
      )}

      {/* Help hint */}
      {connections.length === 0 && sources.length > 0 && consumers.length > 0 && (
        <div className="help-hint" style={{
          position: 'fixed',
          bottom: 16,
          left: 16,
          padding: '10px 14px',
          background: 'rgba(11, 15, 22, 0.9)',
          backdropFilter: 'blur(12px)',
          border: '1px solid rgba(76, 243, 255, 0.3)',
          borderRadius: 10,
          fontFamily: 'JetBrains Mono, monospace',
          fontSize: 10,
          letterSpacing: '0.1em',
          color: 'var(--ink-1)',
          maxWidth: 280
        }}>
          <span style={{ color: 'var(--cyan)' }}>→</span> Drag from a battery's <span style={{ color: 'var(--cyan)' }}>output port</span> to a consumer to connect.
          <br />
          <span style={{ color: 'var(--cyan)' }}>→</span> Click a cable to disconnect.
        </div>
      )}
    </div>
  );
};

// Mount
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
