// HarperSync.jsx — real-time WebSocket sync + REST helpers for Harper persistence
//
// Exposes window.HarperSync with:
//   connect()                    → open WebSocket to /SceneSync
//   loadScenes()                 → GET /Scene/ (initial load)
//   loadAppState()               → GET /AppState/ (active scene + transitions)
//   saveScene(scene, sortOrder)  → send via WS
//   deleteScene(id)              → send via WS
//   saveAppState(activeId, transitions) → send via WS
//   getMe()                      → GET /Me (auth check)

const HarperSync = (() => {
  const BASE = window.location.origin;
  let _ws = null;
  let _onUpdate = null;
  let _reconnectTimer = null;

  // --- JSON fields that need stringify/parse ---
  const JSON_FIELDS = ['canvas', 'blocks', 'accent', 'externals', 'github'];

  function _stringifyScene(scene, sortOrder) {
    const out = { ...scene, sortOrder };
    for (const f of JSON_FIELDS) {
      if (out[f] !== undefined && out[f] !== null && typeof out[f] !== 'string') {
        out[f] = JSON.stringify(out[f]);
      }
    }
    return out;
  }

  function _parseScene(raw) {
    if (!raw) return raw;
    const out = { ...raw };
    for (const f of JSON_FIELDS) {
      if (typeof out[f] === 'string') {
        try { out[f] = JSON.parse(out[f]); } catch {}
      }
    }
    return out;
  }

  function _parseAppState(raw) {
    if (!raw) return raw;
    const out = { ...raw };
    if (typeof out.transitions === 'string') {
      try { out.transitions = JSON.parse(out.transitions); } catch {}
    }
    return out;
  }

  return {
    set onUpdate(fn) { _onUpdate = fn; },

    // --- Auth check ---
    async getMe() {
      try {
        const res = await fetch(`${BASE}/Me`);
        if (!res.ok) return { authenticated: false };
        return await res.json();
      } catch { return { authenticated: false }; }
    },

    // --- REST: initial load ---
    async loadScenes() {
      try {
        const res = await fetch(`${BASE}/Scene/?sort(+sortOrder)`);
        if (!res.ok) return null;
        const data = await res.json();
        if (!Array.isArray(data)) return null;
        return data.map(_parseScene);
      } catch { return null; }
    },

    async loadAppState() {
      try {
        const res = await fetch(`${BASE}/AppState/`);
        if (!res.ok) return null;
        const data = await res.json();
        if (Array.isArray(data) && data.length > 0) return _parseAppState(data[0]);
        return null;
      } catch { return null; }
    },

    // --- WebSocket ---
    connect() {
      if (_ws && _ws.readyState === WebSocket.OPEN) return;
      clearTimeout(_reconnectTimer);

      const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
      _ws = new WebSocket(`${protocol}//${location.host}/SceneSync`);

      _ws.onopen = () => {
        console.log('[HarperSync] WebSocket connected');
      };

      _ws.onmessage = (e) => {
        try {
          const data = JSON.parse(e.data);
          if (_onUpdate) _onUpdate(data);
        } catch {}
      };

      _ws.onclose = () => {
        console.log('[HarperSync] WebSocket closed, reconnecting in 3s...');
        _reconnectTimer = setTimeout(() => this.connect(), 3000);
      };

      _ws.onerror = () => {
        // onclose will fire after this
      };
    },

    _send(msg) {
      if (_ws && _ws.readyState === WebSocket.OPEN) {
        _ws.send(JSON.stringify(msg));
        return true;
      }
      return false;
    },

    saveScene(scene, sortOrder) {
      this._send({
        type: 'saveScene',
        payload: _stringifyScene(scene, sortOrder),
      });
    },

    deleteScene(id) {
      this._send({ type: 'deleteScene', id });
    },

    saveAppState(activeSceneId, transitions) {
      this._send({
        type: 'saveAppState',
        payload: {
          activeSceneId,
          transitions: typeof transitions === 'string' ? transitions : JSON.stringify(transitions || {}),
        },
      });
    },

    disconnect() {
      clearTimeout(_reconnectTimer);
      if (_ws) { _ws.close(); _ws = null; }
    },
  };
})();

window.HarperSync = HarperSync;
