// Store — real data layer connecting to backend API + WebSocket
const { createContext, useContext, useState, useEffect, useRef, useCallback } = React;

const StoreContext = createContext(null);

function useStore() { return useContext(StoreContext); }

function StoreProvider({ children }) {
  const [user, setUser] = useState(null);
  const [authChecked, setAuthChecked] = useState(false);
  const [authRequired, setAuthRequired] = useState(false);
  const [machines, setMachines] = useState([]);
  const [sessions, setSessions] = useState([]);
  const [messages, setMessages] = useState({});
  const [streamBlocks, setStreamBlocks] = useState({});
  const [agentRunStreams, setAgentRunStreams] = useState({});
  const [sessionStatus, setSessionStatus] = useState({});
  const [connected, setConnected] = useState(false);
  const wsRef = useRef(null);
  const clientIdRef = useRef(null);
  const subscribedRef = useRef(new Set());
  const reconnectRef = useRef(null);
  const heartbeatRef = useRef(null);

  // Auth check
  useEffect(() => {
    fetch('/auth/me', { credentials: 'include' })
      .then(r => r.json())
      .then(data => {
        setUser(data.user || null);
        setAuthRequired(data.auth_required !== false);
        setAuthChecked(true);
      })
      .catch(() => { setAuthChecked(true); setAuthRequired(false); });
  }, []);

  // Fetch machines
  const fetchMachines = useCallback(() => {
    fetch('/api/machines', { credentials: 'include' })
      .then(r => r.json())
      .then(data => { if (Array.isArray(data)) setMachines(data); })
      .catch(() => {});
  }, []);

  // Fetch sessions
  const fetchSessions = useCallback(() => {
    fetch('/api/sessions', { credentials: 'include' })
      .then(r => r.json())
      .then(data => { if (Array.isArray(data)) setSessions(data); })
      .catch(() => {});
  }, []);

  // Load data when authenticated
  useEffect(() => {
    if (!authChecked) return;
    if (authRequired && !user) return;
    fetchMachines();
    fetchSessions();
    const iv = setInterval(() => { fetchMachines(); fetchSessions(); }, 15000);
    return () => clearInterval(iv);
  }, [authChecked, user, authRequired]);

  // WebSocket connection
  const connectWs = useCallback(() => {
    if (!authChecked || (authRequired && !user)) return;
    const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
    const ws = new WebSocket(`${proto}//${location.host}/ws`);
    wsRef.current = ws;

    ws.onopen = () => {
      ws.send(JSON.stringify({ type: 'hello', protocolVersions: ['1.0', '1.1'], role: 'web', clientVersion: '2.0.0' }));
    };

    ws.onmessage = (ev) => {
      let msg;
      try { msg = JSON.parse(ev.data); } catch { return; }
      handleWsMessage(msg);
    };

    ws.onclose = () => {
      setConnected(false);
      clearInterval(heartbeatRef.current);
      reconnectRef.current = setTimeout(connectWs, 3000);
    };

    ws.onerror = () => ws.close();
  }, [authChecked, user, authRequired]);

  useEffect(() => {
    connectWs();
    return () => {
      clearTimeout(reconnectRef.current);
      clearInterval(heartbeatRef.current);
      if (wsRef.current) wsRef.current.close();
    };
  }, [connectWs]);

  const handleWsMessage = (msg) => {
    switch (msg.type) {
      case 'connected':
        clientIdRef.current = msg.clientId;
        setConnected(true);
        // Resubscribe
        subscribedRef.current.forEach(sid => {
          wsSend({ type: 'subscribe_session', session_id: sid });
        });
        heartbeatRef.current = setInterval(() => wsSend({ type: 'ping' }), 25000);
        break;
      case 'hello_ok':
        break;
      case 'subscribed':
        if (msg.history) {
          setMessages(prev => ({ ...prev, [msg.session_id]: msg.history }));
        }
        break;
      case 'new_message':
        setMessages(prev => {
          const curr = prev[msg.session_id] || [];
          if (curr.find(m => m.client_message_id === msg.client_message_id)) return prev;
          return { ...prev, [msg.session_id]: [...curr, msg] };
        });
        break;
      case 'stream_event':
        handleStreamEvent(msg);
        break;
      case 'session_status':
        setSessionStatus(prev => ({ ...prev, [msg.session_id]: msg.status }));
        setSessions(prev => prev.map(s => s.id === msg.session_id ? { ...s, status: msg.status } : s));
        break;
      case 'ack':
        break;
      case 'update_status':
        fetchMachines();
        break;
      case 'agent_run_stream':
        setAgentRunStreams(prev => {
          const key = msg.run_id || msg.agent_id;
          const curr = prev[key] || [];
          return { ...prev, [key]: [...curr, msg.event] };
        });
        if (window.__agentRunHandler) window.__agentRunHandler(msg);
        break;
      case 'agent_run_complete':
        // Clear stream for this run
        setAgentRunStreams(prev => {
          const next = { ...prev };
          delete next[msg.run_id];
          return next;
        });
        if (window.__agentRunHandler) window.__agentRunHandler(msg);
        break;
      case 'error':
        console.warn('[WS error]', msg.message);
        break;
    }
  };

  const handleStreamEvent = (msg) => {
    const { session_id, event } = msg;
    if (!event) return;
    setStreamBlocks(prev => {
      const curr = prev[session_id] || [];
      const block = { type: event.event_type, ...event };
      if (event.event_type === 'done') {
        return { ...prev, [session_id]: [] };
      }
      return { ...prev, [session_id]: [...curr, block] };
    });
    if (event.event_type === 'done' || event.event_type === 'text') {
      // Update session status
      if (event.event_type === 'done') {
        setSessionStatus(prev => ({ ...prev, [session_id]: 'idle' }));
        setSessions(prev => prev.map(s => s.id === session_id ? { ...s, status: 'idle' } : s));
      }
    }
  };

  const wsSend = (data) => {
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify(data));
    }
  };

  const subscribe = useCallback((sessionId) => {
    subscribedRef.current.add(sessionId);
    wsSend({ type: 'subscribe_session', session_id: sessionId });
  }, []);

  const unsubscribe = useCallback((sessionId) => {
    subscribedRef.current.delete(sessionId);
    wsSend({ type: 'unsubscribe_session', session_id: sessionId });
  }, []);

  const sendMessage = useCallback((sessionId, content) => {
    const clientMsgId = crypto.randomUUID();
    const msg = {
      type: 'send_message',
      session_id: sessionId,
      client_message_id: clientMsgId,
      content_encrypted: content,
      sender_type: 'user',
      sender_id: clientIdRef.current,
      request_id: crypto.randomUUID(),
    };
    wsSend(msg);
    // Optimistic add
    setMessages(prev => {
      const curr = prev[sessionId] || [];
      return { ...prev, [sessionId]: [...curr, { ...msg, created_at: new Date().toISOString(), status: 'sent' }] };
    });
    setSessionStatus(prev => ({ ...prev, [sessionId]: 'running' }));
    setSessions(prev => prev.map(s => s.id === sessionId ? { ...s, status: 'running' } : s));
  }, []);

  const stopSession = useCallback((sessionId) => {
    wsSend({ type: 'stop_session', session_id: sessionId, request_id: crypto.randomUUID() });
  }, []);

  const createSession = useCallback(async (machineId) => {
    const res = await fetch('/api/sessions', {
      method: 'POST', credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ machine_id: machineId }),
    });
    const session = await res.json();
    if (session.id) {
      setSessions(prev => [...prev, session]);
      subscribe(session.id);
    }
    return session;
  }, [subscribe]);

  const renameSession = useCallback(async (sessionId, name) => {
    await fetch(`/api/sessions/${sessionId}`, {
      method: 'PATCH', credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name }),
    });
    setSessions(prev => prev.map(s => s.id === sessionId ? { ...s, name } : s));
  }, []);

  const archiveSession = useCallback(async (sessionId) => {
    await fetch(`/api/sessions/${sessionId}`, {
      method: 'PATCH', credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ archived: 1 }),
    });
    setSessions(prev => prev.filter(s => s.id !== sessionId));
  }, []);

  const deleteSession = useCallback(async (sessionId) => {
    await fetch(`/api/sessions/${sessionId}`, { method: 'DELETE', credentials: 'include' });
    setSessions(prev => prev.filter(s => s.id !== sessionId));
  }, []);

  const updateMachine = useCallback(async (machineId) => {
    const res = await fetch(`/api/machines/${machineId}/update`, { method: 'POST', credentials: 'include' });
    return res.json();
  }, []);

  const createMachine = useCallback(async (name) => {
    const res = await fetch('/api/machines', {
      method: 'POST', credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name }),
    });
    const machine = await res.json();
    if (machine.id) {
      fetchMachines();
      return machine;
    }
    return null;
  }, [fetchMachines]);

  const deleteMachine = useCallback(async (machineId) => {
    await fetch(`/api/machines/${machineId}`, { method: 'DELETE', credentials: 'include' });
    setMachines(prev => prev.filter(m => m.id !== machineId));
  }, []);

  const value = {
    user, authChecked, authRequired, machines, sessions, messages, streamBlocks, agentRunStreams,
    sessionStatus, connected, subscribe, unsubscribe, sendMessage, stopSession,
    createSession, renameSession, archiveSession, deleteSession, updateMachine,
    createMachine, deleteMachine, fetchMachines, fetchSessions,
  };

  return React.createElement(StoreContext.Provider, { value }, children);
}

window.StoreProvider = StoreProvider;
window.useStore = useStore;
