// 웹 푸시(Web Push) — PWA 설치 사용자에게 알림 발송.
// VAPID 키는 env(VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY) 우선, 없으면 하드코딩 기본값 사용
// (이 프로젝트는 AES_KEY/MASTER_PWD 등 비밀값을 constants 에 하드코딩하는 관례를 따름).
import webpush from "web-push";
import { pool } from "./db";

// 기본 VAPID 키 — 운영에서 .env.production 에 VAPID_* 를 넣으면 그 값이 우선한다.
const VAPID_PUBLIC_KEY =
  process.env.VAPID_PUBLIC_KEY ||
  "BGTqaBRflu1yaLssXymN9fb2QEudKEI1FwbsvG0h7Vz768AYERVAuTvG2A6ZQ7FAqbx9P6o_fvmwTgCQD6aFhO8";
const VAPID_PRIVATE_KEY =
  process.env.VAPID_PRIVATE_KEY || "825izwQffsYwmv1r-my7T-DA0ROLTgZ2yqI2iQYwtV4";
const VAPID_SUBJECT = process.env.VAPID_SUBJECT || "mailto:admin@momotogether.com";

let configured = false;
function ensureConfigured() {
  if (configured) return;
  webpush.setVapidDetails(VAPID_SUBJECT, VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY);
  configured = true;
}

export function getVapidPublicKey(): string {
  return VAPID_PUBLIC_KEY;
}

let tableEnsured = false;
export async function ensurePushTable() {
  if (tableEnsured) return;
  try {
    await pool.query(`
      CREATE TABLE IF NOT EXISTS momo_push_subscriptions (
        objid       TEXT PRIMARY KEY,
        user_id     TEXT,
        endpoint    TEXT UNIQUE NOT NULL,
        p256dh      TEXT NOT NULL,
        auth        TEXT NOT NULL,
        user_agent  TEXT,
        regdate     TIMESTAMP DEFAULT NOW(),
        last_seen   TIMESTAMP DEFAULT NOW()
      );
    `);
    tableEnsured = true;
  } catch (err) {
    console.error("[push/ensurePushTable]", err);
  }
}

export interface PushPayload {
  title: string;
  body: string;
  url?: string;   // 클릭 시 열 경로 (기본 /m/orders/new)
  tag?: string;   // 같은 tag 알림은 묶임
}

interface SubRow {
  objid: string;
  endpoint: string;
  p256dh: string;
  auth: string;
}

// 구독 목록에게 발송. 만료(404/410) 구독은 자동 삭제.
//  - userIds 지정 시 해당 사용자만.
//  - 미지정 + generalOnly 시 일반 사용자(관리자 user_type='A' 제외) 전체.
//  - 미지정 + !generalOnly 시 전체 구독자.
export async function sendPush(
  payload: PushPayload,
  userIds?: string[],
  opts?: { generalOnly?: boolean }
): Promise<{ sent: number; failed: number }> {
  ensureConfigured();
  await ensurePushTable();

  let rows: SubRow[];
  if (userIds && userIds.length > 0) {
    const ph = userIds.map((_, i) => `$${i + 1}`).join(",");
    const res = await pool.query<SubRow>(
      `SELECT objid, endpoint, p256dh, auth FROM momo_push_subscriptions WHERE user_id IN (${ph})`,
      userIds
    );
    rows = res.rows;
  } else if (opts?.generalOnly) {
    // 일반 사용자만 — 관리자(user_type='A') 구독은 제외
    const res = await pool.query<SubRow>(
      `SELECT s.objid, s.endpoint, s.p256dh, s.auth
         FROM momo_push_subscriptions s
         LEFT JOIN user_info u ON u.user_id = s.user_id
        WHERE UPPER(COALESCE(u.user_type, '')) <> 'A'`
    );
    rows = res.rows;
  } else {
    const res = await pool.query<SubRow>(`SELECT objid, endpoint, p256dh, auth FROM momo_push_subscriptions`);
    rows = res.rows;
  }

  const body = JSON.stringify({
    title: payload.title,
    body: payload.body,
    url: payload.url || "/m/orders/new",
    tag: payload.tag,
  });

  let sent = 0;
  let failed = 0;
  const stale: string[] = [];

  await Promise.all(
    rows.map(async (r) => {
      try {
        await webpush.sendNotification(
          { endpoint: r.endpoint, keys: { p256dh: r.p256dh, auth: r.auth } },
          body
        );
        sent++;
      } catch (err: unknown) {
        failed++;
        const status = (err as { statusCode?: number })?.statusCode;
        if (status === 404 || status === 410) stale.push(r.objid); // 만료 구독
      }
    })
  );

  if (stale.length > 0) {
    const ph = stale.map((_, i) => `$${i + 1}`).join(",");
    await pool.query(`DELETE FROM momo_push_subscriptions WHERE objid IN (${ph})`, stale).catch(() => {});
  }

  console.log(`[push] targets=${rows.length} sent=${sent} failed=${failed} stale=${stale.length} title="${payload.title}"`);
  return { sent, failed };
}

// 구독 현황 카운트 (진단용)
export async function pushSubCounts(): Promise<{ total: number; general: number; admin: number }> {
  await ensurePushTable();
  const res = await pool.query<{ total: string; admin: string }>(
    `SELECT COUNT(*)::int::text AS total,
            COUNT(*) FILTER (WHERE UPPER(COALESCE(u.user_type,'')) = 'A')::int::text AS admin
       FROM momo_push_subscriptions s
       LEFT JOIN user_info u ON u.user_id = s.user_id`
  );
  const total = Number(res.rows[0]?.total ?? 0);
  const admin = Number(res.rows[0]?.admin ?? 0);
  return { total, general: total - admin, admin };
}
