// Web Push for goal/kickoff/full-time alerts on a followed team. Entirely
// opt-in and feature-flagged: without VAPID keys in the environment the API
// reports disabled, the client hides its UI, and nothing else changes.
import webpush from 'web-push';
import { addPushSub, pushSubsForTeams, removePushSub } from './db/db';
import type { Fixture } from '../../src/lib/types';

const PUBLIC_KEY = process.env.VAPID_PUBLIC_KEY?.trim() ?? '';
const PRIVATE_KEY = process.env.VAPID_PRIVATE_KEY?.trim() ?? '';
const SUBJECT = process.env.VAPID_SUBJECT?.trim() || 'mailto:nilsbriggen@gmail.com';

export const pushEnabled = Boolean(PUBLIC_KEY && PRIVATE_KEY);
export const vapidPublicKey = PUBLIC_KEY;

if (pushEnabled) {
  webpush.setVapidDetails(SUBJECT, PUBLIC_KEY, PRIVATE_KEY);
  console.log('[push] web push enabled');
}

export function subscribe(subscription: { endpoint: string }, team: string): void {
  addPushSub(subscription.endpoint, JSON.stringify(subscription), team);
}

export function unsubscribe(endpoint: string): void {
  removePushSub(endpoint);
}

async function send(team: string[], title: string, body: string, url: string): Promise<void> {
  if (!pushEnabled) return;
  const subs = pushSubsForTeams(team);
  await Promise.allSettled(
    subs.map(async (s) => {
      try {
        await webpush.sendNotification(JSON.parse(s.json), JSON.stringify({ title, body, url }));
      } catch (e) {
        const code = (e as { statusCode?: number }).statusCode;
        if (code === 404 || code === 410) removePushSub(s.endpoint); // gone — clean up
      }
    }),
  );
}

interface ScoreState { home: number | null; away: number | null; status: string }

/** Diff fixture states between broadcasts and push kickoff/goal/full-time
 *  alerts to followers of either team. State lives in-memory: after a restart
 *  the first broadcast just primes the cache (no notification replays). */
export class PushNotifier {
  private last = new Map<number, ScoreState>();
  private primed = false;

  tick(fixtures: Fixture[]): void {
    if (!pushEnabled) return;
    const interesting = fixtures.filter((f) => f.status !== 'scheduled' && f.home.team && f.away.team);
    if (!this.primed) {
      for (const f of interesting) this.last.set(f.num, { home: f.homeScore, away: f.awayScore, status: f.status });
      this.primed = true;
      return;
    }
    for (const f of interesting) {
      const prev = this.last.get(f.num);
      const cur: ScoreState = { home: f.homeScore, away: f.awayScore, status: f.status };
      this.last.set(f.num, cur);
      const home = f.home.team!;
      const away = f.away.team!;
      const teams = [home, away];
      const url = `/match/${f.num}`;
      const score = `${cur.home ?? 0}–${cur.away ?? 0}`;
      if (!prev) {
        if (f.status === 'live') void send(teams, `Kickoff: ${home} vs ${away}`, 'The match is underway.', url);
        continue;
      }
      if (prev.status !== 'live' && f.status === 'live') {
        void send(teams, `Kickoff: ${home} vs ${away}`, 'The match is underway.', url);
      }
      if ((cur.home ?? 0) + (cur.away ?? 0) > (prev.home ?? 0) + (prev.away ?? 0)) {
        void send(teams, `Goal! ${home} ${score} ${away}`, f.minute ? `${f.minute}'` : '', url);
      }
      if (prev.status === 'live' && f.status === 'finished') {
        void send(teams, `Full time: ${home} ${score} ${away}`, '', url);
      }
    }
  }
}
