// Supabase Realtime — subscribe to table row changes
// Used for live multiplayer in Chess and Ludo

import * as store from './store.js';
import { SUPABASE_URL } from './config.js';

const REALTIME_URL = SUPABASE_URL.replace('https://', 'wss://') + '/realtime/v1/websocket';
const ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjE4OTM0NTYwMDB9.31PF6PvP-pSrvRuQwLFptQoejR0W1A7o53lZhEbnz84';

let ws = null;
let heartbeatInterval = null;
let ref = 0;
let subscriptions = {};
let reconnectTimer = null;
let connected = false;
let reconnectDelay = 1000;

export function connect() {
  if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return;

  const token = store.get('auth.token') || ANON_KEY;
  ws = new WebSocket(`${REALTIME_URL}?apikey=${ANON_KEY}&token=${token}&vsn=1.0.0`);

  ws.onopen = () => {
    connected = true;
    reconnectDelay = 1000;
    clearTimeout(reconnectTimer);
    heartbeatInterval = setInterval(() => {
      send({ topic: 'phoenix', event: 'heartbeat', payload: {}, ref: String(++ref) });
    }, 30000);
    // Re-subscribe all active subscriptions
    Object.keys(subscriptions).forEach(topic => joinChannel(topic));
  };

  ws.onmessage = (e) => {
    try {
      const msg = JSON.parse(e.data);
      if (msg.event === 'postgres_changes') {
        // Supabase Realtime nests the row under payload.data; older/self-hosted
        // builds put it directly on payload. Accept both rather than silently
        // handing every subscriber `undefined`.
        const p = msg.payload || {};
        const body = p.data || p;
        const callbacks = subscriptions[msg.topic];
        if (callbacks) {
          callbacks.forEach(cb => cb({
            type: body.type || body.eventType, // INSERT, UPDATE, DELETE
            new: body.record ?? body.new,
            old: body.old_record ?? body.old
          }));
        }
      } else if (msg.event === 'phx_reply' && msg.payload && msg.payload.status === 'error') {
        // A refused channel join used to fail silently, leaving the client
        // convinced it was subscribed and waiting forever for updates.
        console.warn('[realtime] channel join refused:', msg.topic, msg.payload.response);
      }
    } catch (err) {
      console.warn('[realtime] onmessage error:', err);
    }
  };

  ws.onclose = () => {
    connected = false;
    ws = null;
    clearInterval(heartbeatInterval);
    reconnectTimer = setTimeout(() => connect(), reconnectDelay);
    reconnectDelay = Math.min(reconnectDelay * 2, 30000);
  };

  ws.onerror = () => {
    ws.close();
  };
}

export function disconnect() {
  clearInterval(heartbeatInterval);
  clearTimeout(reconnectTimer);
  if (ws) { ws.close(); ws = null; }
  connected = false;
  subscriptions = {};
}

export function subscribe(table, filter, callback) {
  const topic = `realtime:public:${table}:${filter}`;

  if (!subscriptions[topic]) {
    subscriptions[topic] = [];
  }
  subscriptions[topic].push(callback);

  if (connected) joinChannel(topic);
  else connect();

  // Return unsubscribe function
  return () => {
    subscriptions[topic] = subscriptions[topic].filter(cb => cb !== callback);
    if (subscriptions[topic].length === 0) {
      leaveChannel(topic);
      delete subscriptions[topic];
    }
  };
}

// Subscribe to a specific match row
export function subscribeMatch(matchId, callback) {
  return subscribe('matches', `id=eq.${matchId}`, callback);
}

// Subscribe to a specific ludo match row
export function subscribeLudoMatch(matchId, callback) {
  return subscribe('ludo_matches', `id=eq.${matchId}`, callback);
}

// Subscribe to a specific backgammon match row
export function subscribeBackgammonMatch(matchId, callback) {
  return subscribe('backgammon_matches', `id=eq.${matchId}`, callback);
}

// Subscribe to matchmaking queue for match found
export function subscribeQueue(playerId, callback) {
  return subscribe('matchmaking_queue', `player_id=eq.${playerId}`, callback);
}

// Subscribe to tournament round changes for a specific tournament
export function subscribeTournamentRounds(tournamentId, callback) {
  return subscribe('el3ab_tournament_rounds', `tournament_id=eq.${tournamentId}`, callback);
}

function joinChannel(topic) {
  const token = store.get('auth.token') || ANON_KEY;
  send({
    topic,
    event: 'phx_join',
    payload: {
      config: {
        broadcast: { self: false },
        postgres_changes: [{
          event: '*',
          schema: 'public',
          table: topic.split(':')[2],
          filter: topic.split(':')[3]
        }]
      },
      access_token: token
    },
    ref: String(++ref)
  });
}

function leaveChannel(topic) {
  send({ topic, event: 'phx_leave', payload: {}, ref: String(++ref) });
}

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

export function isConnected() { return connected; }
