/**
 * Web Push delivery.
 *
 * One-direction POST to the user's browser push service (FCM, Mozilla, Apple).
 * Subscriptions are stored in push_subscriptions (migration 015) and the
 * service worker in web/src/sw.ts handles the `push` event.
 *
 * A 410 (Gone) or 404 response means the subscription is dead — we delete
 * the row so we stop trying. Anything else is logged and treated as transient.
 */

const webpush = require('web-push');
const { getSupabaseServiceClient } = require('../../utils/supabase');

const VAPID_PUBLIC = process.env.VAPID_PUBLIC_KEY;
const VAPID_PRIVATE = process.env.VAPID_PRIVATE_KEY;
const VAPID_SUBJECT = process.env.VAPID_SUBJECT || 'mailto:contact@vyndr.app';

let _initialized = false;
function ensureInit() {
  if (_initialized) return true;
  if (!VAPID_PUBLIC || !VAPID_PRIVATE) return false;
  webpush.setVapidDetails(VAPID_SUBJECT, VAPID_PUBLIC, VAPID_PRIVATE);
  _initialized = true;
  return true;
}

function configured() {
  return !!(VAPID_PUBLIC && VAPID_PRIVATE);
}

function rowToSubscription(row) {
  return {
    endpoint: row.endpoint,
    keys: { p256dh: row.keys_p256dh, auth: row.keys_auth },
  };
}

async function deleteSubscription(supabase, subscriptionId) {
  await supabase.from('push_subscriptions').delete().eq('id', subscriptionId);
}

async function sendOne(supabase, row, payload) {
  try {
    await webpush.sendNotification(rowToSubscription(row), JSON.stringify(payload));
    return { ok: true, id: row.id };
  } catch (err) {
    const status = err?.statusCode;
    if (status === 404 || status === 410) {
      await deleteSubscription(supabase, row.id);
      return { ok: false, id: row.id, pruned: true };
    }
    console.warn('[webPush] send failed:', { id: row.id, status, message: err?.message });
    return { ok: false, id: row.id, error: err?.message };
  }
}

async function sendPushToUser(userId, notification) {
  if (!configured() || !ensureInit()) {
    return { ok: false, error: 'VAPID keys not configured' };
  }
  const supabase = getSupabaseServiceClient();
  const { data: rows, error } = await supabase
    .from('push_subscriptions')
    .select('id, endpoint, keys_p256dh, keys_auth')
    .eq('user_id', userId);
  if (error) return { ok: false, error: error.message };
  if (!rows || rows.length === 0) return { ok: true, sent: 0 };

  const results = await Promise.allSettled(rows.map((row) => sendOne(supabase, row, notification)));
  const summary = results.reduce(
    (acc, r) => {
      if (r.status === 'fulfilled' && r.value.ok) acc.sent += 1;
      else if (r.status === 'fulfilled' && r.value.pruned) acc.pruned += 1;
      else acc.failed += 1;
      return acc;
    },
    { sent: 0, pruned: 0, failed: 0 }
  );
  return { ok: true, ...summary };
}

async function sendPushToSport(sport, notification, opts = {}) {
  if (!configured() || !ensureInit()) {
    return { ok: false, error: 'VAPID keys not configured' };
  }
  const { kind } = opts;
  const supabase = getSupabaseServiceClient();
  let query = supabase
    .from('push_subscriptions')
    .select('id, endpoint, keys_p256dh, keys_auth')
    .contains('sport_preferences', [sport]);
  if (kind === 'resolution') query = query.eq('notify_on_resolution', true);
  if (kind === 'cascade') query = query.eq('notify_on_cascade', true);
  if (kind === 'cheatsheet') query = query.eq('notify_on_cheatsheet', true);

  const { data: rows, error } = await query;
  if (error) return { ok: false, error: error.message };
  if (!rows || rows.length === 0) return { ok: true, sent: 0 };

  const results = await Promise.allSettled(rows.map((row) => sendOne(supabase, row, notification)));
  const summary = results.reduce(
    (acc, r) => {
      if (r.status === 'fulfilled' && r.value.ok) acc.sent += 1;
      else if (r.status === 'fulfilled' && r.value.pruned) acc.pruned += 1;
      else acc.failed += 1;
      return acc;
    },
    { sent: 0, pruned: 0, failed: 0 }
  );
  return { ok: true, ...summary };
}

async function cleanupExpired() {
  // Called from a scheduled task. Walks every subscription and pings the
  // push service with an empty payload — anything that 410s gets pruned.
  // For now a no-op stub; cleanup happens lazily on send failures above.
  return { ok: true, note: 'lazy cleanup runs on send failures' };
}

module.exports = {
  configured,
  sendPushToUser,
  sendPushToSport,
  cleanupExpired,
};
