/**
 * OPS 이메일 세션 전체 무효화(모든 디바이스 로그아웃)
 * 쿠키에 iat(발급 시각)를 넣고, DB sessions_revoked_at 이후 iat만 유효하게 한다.
 */

const REVOKE_FILE = "ops-session-revocations.json";

/**
 * @param {string} email
 * @returns {string}
 */
function normalizeEmail(email) {
  return String(email || "")
    .trim()
    .toLowerCase()
    .slice(0, 320);
}

/**
 * @param {import('pg').Pool | null | undefined} pgPool
 * @param {string} dataDir
 * @param {string} email
 * @returns {Promise<number>} revoked at epoch ms, 0 if never revoked
 */
async function getSessionsRevokedAtMs(pgPool, dataDir, email) {
  const e = normalizeEmail(email);
  if (!e) return 0;

  if (pgPool) {
    try {
      const r = await pgPool.query(
        `SELECT sessions_revoked_at FROM ops_email_users WHERE email = $1`,
        [e]
      );
      const ts = r.rows?.[0]?.sessions_revoked_at;
      return ts ? new Date(ts).getTime() : 0;
    } catch (err) {
      console.error("[ops-session-revoke] get failed:", err.message);
      return 0;
    }
  }

  try {
    const fs = require("fs");
    const path = require("path");
    const fp = path.join(dataDir, REVOKE_FILE);
    if (!fs.existsSync(fp)) return 0;
    const raw = fs.readFileSync(fp, "utf8");
    const map = JSON.parse(raw);
    const v = map?.[e];
    return typeof v === "number" && Number.isFinite(v) ? v : 0;
  } catch {
    return 0;
  }
}

/**
 * @param {import('pg').Pool | null | undefined} pgPool
 * @param {string} dataDir
 * @param {string} email
 * @param {number} iatMs
 * @returns {Promise<boolean>}
 */
async function isOpsSessionRevoked(pgPool, dataDir, email, iatMs) {
  const revokedAt = await getSessionsRevokedAtMs(pgPool, dataDir, email);
  if (!revokedAt) return false;
  const iat = typeof iatMs === "number" && Number.isFinite(iatMs) ? iatMs : 0;
  return iat <= revokedAt;
}

/**
 * @param {import('pg').Pool | null | undefined} pgPool
 * @param {string} dataDir
 * @param {string} email
 * @returns {Promise<{ revokedAtMs: number }>}
 */
async function revokeAllOpsSessionsForEmail(pgPool, dataDir, email) {
  const e = normalizeEmail(email);
  if (!e) {
    throw new Error("email is required");
  }
  const revokedAtMs = Date.now();

  if (pgPool) {
    await pgPool.query(
      `INSERT INTO ops_email_users (email, first_seen_at, last_login_at, login_count, sessions_revoked_at)
       VALUES ($1, NOW(), NOW(), 0, to_timestamp($2 / 1000.0))
       ON CONFLICT (email) DO UPDATE SET sessions_revoked_at = EXCLUDED.sessions_revoked_at`,
      [e, revokedAtMs]
    );
    return { revokedAtMs };
  }

  const fs = require("fs");
  const path = require("path");
  const fp = path.join(dataDir, REVOKE_FILE);
  let map = {};
  try {
    if (fs.existsSync(fp)) {
      map = JSON.parse(fs.readFileSync(fp, "utf8")) || {};
    }
  } catch {
    map = {};
  }
  map[e] = revokedAtMs;
  fs.mkdirSync(path.dirname(fp), { recursive: true });
  fs.writeFileSync(fp, JSON.stringify(map, null, 2), "utf8");
  return { revokedAtMs };
}

module.exports = {
  getSessionsRevokedAtMs,
  isOpsSessionRevoked,
  revokeAllOpsSessionsForEmail,
};
