/**
 * WebPushNotificationAdapter — system-communications-notifications (Task 8).
 *
 * Delivers notifications via Web Push (RFC 8030/8292).
 * `canDeliver` is true iff the user has ≥1 push_subscriptions row.
 * `deliver` fans out across all subs, prunes 410 Gone subs, returns aggregated result.
 */
import type {
  NotificationAdapter,
  DeliverableNotification,
  DeliveryResult,
  NotificationType,
} from '@zync/types'
import type { Db } from '@zync/db/queries'
import type { Env } from '@zync/types'
import {
  countPushSubscriptionsForUser,
  getPushSubscriptionsForUser,
  deletePushSubscriptionByEndpoint,
} from '@zync/db/queries'
import { sendWebPush } from '../web-push/send'

export class WebPushNotificationAdapter implements NotificationAdapter {
  readonly id = 'push' as const

  constructor(
    private readonly db: Db,
    private readonly env: Env,
    private readonly tenantId: string,
  ) {}

  async canDeliver(userId: string, _type: NotificationType): Promise<boolean> {
    const count = await countPushSubscriptionsForUser(this.db, userId, this.tenantId)
    return count > 0
  }

  async deliver(userId: string, notification: DeliverableNotification): Promise<DeliveryResult> {
    try {
      const subs = await getPushSubscriptionsForUser(this.db, userId, this.tenantId)
      if (subs.length === 0) return { delivered: false, error: 'No push subscriptions' }

      const payload = {
        title: notification.title,
        body: notification.body,
        url: notification.entityId
          ? `/${notification.entityType ?? 'items'}/${notification.entityId}`
          : '/',
        tag: `${notification.type}_${notification.entityId ?? userId}`,
        type: notification.type,
      }

      const results = await Promise.allSettled(
        subs.map((sub) =>
          sendWebPush(
            {
              endpoint: sub.endpoint,
              p256dh: sub.p256dh,
              auth: sub.auth,
            },
            payload,
            this.env.VAPID_PRIVATE_KEY,
            this.env.VAPID_PUBLIC_KEY,
          ),
        ),
      )

      // Prune subscriptions that returned 410 Gone
      const pruneOps: Promise<void>[] = []
      for (const result of results) {
        if (result.status === 'fulfilled' && result.value.gone) {
          pruneOps.push(deletePushSubscriptionByEndpoint(this.db, result.value.endpoint))
        }
      }
      if (pruneOps.length > 0) {
        await Promise.allSettled(pruneOps)
      }

      const anyDelivered = results.some(
        (r) => r.status === 'fulfilled' && !r.value.gone && r.value.status < 300,
      )

      return { delivered: anyDelivered }
    } catch (err) {
      return {
        delivered: false,
        error: err instanceof Error ? err.message : String(err),
      }
    }
  }
}

/** Prune expired subscriptions that returned 410 Gone. */
export async function pruneExpiredSubscriptions(db: Db, endpoints: string[]): Promise<void> {
  await Promise.allSettled(endpoints.map((ep) => deletePushSubscriptionByEndpoint(db, ep)))
}
