// EL3AB Realtime - Supabase Realtime client via raw WebSocket (no npm)

const Realtime = {
  ws: null,
  token: null,
  topic: null,
  callback: null,
  heartbeatInterval: null,
  heartbeatRef: 0,
  awaitingHeartbeat: false,
  reconnectAttempts: 0,
  maxReconnectAttempts: 5,
  reconnectTimer: null,
  matchId: null,
  joined: false,

  ENDPOINT: 'wss://safe-supabase-kong.caprover.al-arcade.com/realtime/v1/websocket',
  ANON_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjE4OTM0NTYwMDB9.31PF6PvP-pSrvRuQwLFptQoejR0W1A7o53lZhEbnz84',
  HEARTBEAT_MS: 30000,

  connect(token) {
    this.token = token;
    this.reconnectAttempts = 0;
    this._open();
  },

  _open() {
    if (this.ws) {
      this.ws.onclose = null;
      this.ws.close();
    }

    const url = this.ENDPOINT + '?apikey=' + this.ANON_KEY + '&vsn=1.0.0';
    this.ws = new WebSocket(url);
    this.joined = false;

    this.ws.onopen = () => {
      console.log('[Realtime] connected');
      this.reconnectAttempts = 0;
      this._startHeartbeat();
      if (this.matchId && this.callback) {
        this._join();
      }
    };

    this.ws.onmessage = (evt) => {
      this._handleMessage(evt);
    };

    this.ws.onclose = () => {
      console.log('[Realtime] disconnected');
      this._stopHeartbeat();
      this.joined = false;
      this._scheduleReconnect();
    };

    this.ws.onerror = (err) => {
      console.error('[Realtime] error', err);
    };
  },

  _handleMessage(evt) {
    let msg;
    try {
      msg = JSON.parse(evt.data);
    } catch (e) {
      return;
    }

    // Heartbeat reply
    if (msg.topic === 'phoenix' && msg.event === 'phx_reply') {
      this.awaitingHeartbeat = false;
      return;
    }

    // Join reply
    if (msg.event === 'phx_reply' && msg.topic === this.topic) {
      if (msg.payload && msg.payload.status === 'ok') {
        this.joined = true;
        console.log('[Realtime] subscribed to', this.topic);
      } else {
        console.error('[Realtime] join failed', msg.payload);
      }
      return;
    }

    // Postgres changes event
    if (msg.event === 'postgres_changes' && msg.topic === this.topic) {
      const payload = msg.payload;
      if (payload && payload.data && payload.data.type === 'UPDATE' && payload.data.record) {
        if (this.callback) {
          this.callback(payload.data.record);
        }
      }
      return;
    }
  },

  _startHeartbeat() {
    this._stopHeartbeat();
    this.heartbeatInterval = setInterval(() => {
      if (this.awaitingHeartbeat) {
        console.warn('[Realtime] heartbeat timeout, reconnecting');
        this.ws.close();
        return;
      }
      this.awaitingHeartbeat = true;
      this.heartbeatRef++;
      this._send({
        topic: 'phoenix',
        event: 'heartbeat',
        payload: {},
        ref: String(this.heartbeatRef)
      });
    }, this.HEARTBEAT_MS);
  },

  _stopHeartbeat() {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      this.heartbeatInterval = null;
    }
    this.awaitingHeartbeat = false;
  },

  _scheduleReconnect() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error('[Realtime] max reconnect attempts reached');
      return;
    }
    const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
    this.reconnectAttempts++;
    console.log('[Realtime] reconnecting in', delay, 'ms (attempt', this.reconnectAttempts + ')');
    this.reconnectTimer = setTimeout(() => {
      this._open();
    }, delay);
  },

  _join() {
    const topic = 'realtime:public:matches:id=eq.' + this.matchId;
    this.topic = topic;
    this._send({
      topic: topic,
      event: 'phx_join',
      payload: {
        config: {
          broadcast: { self: false },
          presence: { key: '' },
          postgres_changes: [{
            event: 'UPDATE',
            schema: 'public',
            table: 'matches',
            filter: 'id=eq.' + this.matchId
          }]
        },
        access_token: this.token
      },
      ref: '1'
    });
  },

  _send(msg) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(msg));
    }
  },

  subscribe(matchId, callback) {
    this.matchId = matchId;
    this.callback = callback;
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this._join();
    }
  },

  unsubscribe() {
    if (this.topic && this.ws && this.ws.readyState === WebSocket.OPEN) {
      this._send({
        topic: this.topic,
        event: 'phx_leave',
        payload: {},
        ref: '2'
      });
    }
    this.topic = null;
    this.matchId = null;
    this.callback = null;
    this.joined = false;
  },

  disconnect() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    this._stopHeartbeat();
    this.reconnectAttempts = this.maxReconnectAttempts; // prevent auto-reconnect
    if (this.ws) {
      this.ws.onclose = null;
      this.ws.close();
      this.ws = null;
    }
    this.topic = null;
    this.matchId = null;
    this.callback = null;
    this.joined = false;
    this.token = null;
    console.log('[Realtime] closed');
  }
};
