/**
 * admin-relay.ts — Admin endpoints for the relay account whitelist + stats.
 *
 * All routes are protected by ADMIN_SECRET env var (Bearer token).
 * If ADMIN_SECRET is not set, the routes reject all requests in production
 * and accept only from localhost in development.
 *
 * Routes:
 *   GET  /api/admin/relay/stats                   — event counts + account total
 *   GET  /api/admin/relay/accounts                — list all relay accounts
 *   POST /api/admin/relay/accounts/:pubkey/grant  — grant access to a pubkey
 *   POST /api/admin/relay/accounts/:pubkey/revoke — revoke access from a pubkey
 */

import { Router, type Request, type Response, type NextFunction } from "express";
import { makeLogger } from "../lib/logger.js";
import { relayAccountService } from "../lib/relay-accounts.js";
import { RELAY_ACCESS_LEVELS, type RelayAccessLevel, db, relayEventQueue, relayAccounts, nostrIdentities } from "@workspace/db";
import { and, eq, gte, inArray, sql } from "drizzle-orm";

const logger = makeLogger("admin-relay");
const router = Router();

// ADMIN_TOKEN is the canonical env var; ADMIN_SECRET is the backward-compat alias.
const ADMIN_TOKEN = process.env["ADMIN_TOKEN"] ?? process.env["ADMIN_SECRET"] ?? "";
const IS_PROD = process.env["NODE_ENV"] === "production";

if (!ADMIN_TOKEN) {
  if (IS_PROD) {
    logger.error(
      "ADMIN_TOKEN is not set in production — admin relay routes are unprotected. " +
      "Set ADMIN_TOKEN in the API server environment immediately.",
    );
  } else {
    logger.warn("ADMIN_TOKEN not set — admin relay routes accept local-only requests (dev mode)");
  }
}

// ── Admin auth middleware ──────────────────────────────────────────────────────
// Shared by all admin-relay*.ts routes. Token is matched against ADMIN_TOKEN
// (env var), falling back to localhost-only in dev when no token is set.

export function requireAdmin(req: Request, res: Response, next: NextFunction): void {
  if (ADMIN_TOKEN) {
    const authHeader = req.headers["authorization"] ?? "";
    const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7).trim() : "";
    if (token !== ADMIN_TOKEN) {
      res.status(401).json({ error: "Unauthorized" });
      return;
    }
  } else {
    const ip = req.ip ?? "";
    const isLocal = ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1";
    if (!isLocal) {
      logger.warn("admin-relay: no token configured, rejecting non-local call", { ip });
      res.status(401).json({ error: "Unauthorized" });
      return;
    }
  }
  next();
}

// ── GET /admin/relay/stats ────────────────────────────────────────────────────
// Returns event counts by status for today + all-time pending, plus account total.

router.get("/admin/relay/stats", requireAdmin, async (_req: Request, res: Response) => {
  const todayStart = new Date();
  todayStart.setUTCHours(0, 0, 0, 0);

  const STRFRY_URL = process.env["STRFRY_URL"] ?? "http://strfry:7777";

  const [queueCounts, accountCount, approvedToday, strfryStats] = await Promise.all([
    db
      .select({ status: relayEventQueue.status, count: sql<number>`count(*)::int` })
      .from(relayEventQueue)
      .groupBy(relayEventQueue.status),
    db.select({ count: sql<number>`count(*)::int` }).from(relayAccounts),
    db
      .select({ count: sql<number>`count(*)::int` })
      .from(relayEventQueue)
      .where(
        and(
          inArray(relayEventQueue.status, ["approved", "auto_approved"]),
          gte(relayEventQueue.decidedAt, todayStart),
        ),
      ),
    // Attempt to fetch live connection count from strfry's /stats endpoint.
    // Strfry exposes /stats in some builds; gracefully return null if unavailable.
    (async (): Promise<{ connections?: number } | null> => {
      try {
        const r = await fetch(`${STRFRY_URL}/stats`, {
          signal: AbortSignal.timeout(2000),
        });
        if (!r.ok) return null;
        return (await r.json()) as { connections?: number };
      } catch {
        return null;
      }
    })(),
  ]);

  const statusMap: Record<string, number> = {};
  for (const row of queueCounts) {
    statusMap[row.status] = row.count;
  }

  res.json({
    pending: statusMap["pending"] ?? 0,
    approved: statusMap["approved"] ?? 0,
    autoApproved: statusMap["auto_approved"] ?? 0,
    rejected: statusMap["rejected"] ?? 0,
    approvedToday: approvedToday[0]?.count ?? 0,
    totalAccounts: accountCount[0]?.count ?? 0,
    liveConnections: strfryStats?.connections ?? null,
  });
});

// ── GET /admin/relay/accounts ─────────────────────────────────────────────────

router.get("/admin/relay/accounts", requireAdmin, async (_req: Request, res: Response) => {
  // LEFT JOIN nostr_identities to include trustTier per account.
  const rows = await db
    .select({
      pubkey:      relayAccounts.pubkey,
      accessLevel: relayAccounts.accessLevel,
      grantedBy:   relayAccounts.grantedBy,
      notes:       relayAccounts.notes,
      grantedAt:   relayAccounts.grantedAt,
      revokedAt:   relayAccounts.revokedAt,
      trustTier:   nostrIdentities.tier,
    })
    .from(relayAccounts)
    .leftJoin(nostrIdentities, eq(relayAccounts.pubkey, nostrIdentities.pubkey))
    .orderBy(relayAccounts.grantedAt);

  res.json({ accounts: rows });
});

// ── POST /admin/relay/accounts/:pubkey/grant ──────────────────────────────────

router.post(
  "/admin/relay/accounts/:pubkey/grant",
  requireAdmin,
  async (req: Request, res: Response) => {
    const { pubkey } = req.params as { pubkey: string };

    if (!pubkey || pubkey.length !== 64 || !/^[0-9a-f]+$/.test(pubkey)) {
      res.status(400).json({ error: "pubkey must be a 64-char lowercase hex string" });
      return;
    }

    const body = req.body as { level?: string; notes?: string };
    const level = (body.level ?? "write").toLowerCase() as RelayAccessLevel;

    if (!RELAY_ACCESS_LEVELS.includes(level)) {
      res.status(400).json({
        error: `Invalid access level '${level}'. Must be one of: ${RELAY_ACCESS_LEVELS.join(", ")}`,
      });
      return;
    }

    const notes = typeof body.notes === "string" ? body.notes : "admin grant";

    await relayAccountService.grant(pubkey, level, notes, "manual");

    logger.info("admin granted relay access", {
      pubkey: pubkey.slice(0, 8),
      level,
      notes,
    });

    res.json({ ok: true, pubkey, accessLevel: level, notes });
  },
);

// ── POST /admin/relay/accounts/:pubkey/revoke ─────────────────────────────────

router.post(
  "/admin/relay/accounts/:pubkey/revoke",
  requireAdmin,
  async (req: Request, res: Response) => {
    const { pubkey } = req.params as { pubkey: string };

    if (!pubkey || pubkey.length !== 64 || !/^[0-9a-f]+$/.test(pubkey)) {
      res.status(400).json({ error: "pubkey must be a 64-char lowercase hex string" });
      return;
    }

    const body = req.body as { reason?: string };
    const reason = typeof body.reason === "string" ? body.reason : "admin revoke";

    await relayAccountService.revoke(pubkey, reason);

    logger.info("admin revoked relay access", {
      pubkey: pubkey.slice(0, 8),
      reason,
    });

    res.json({ ok: true, pubkey, revokedAt: new Date().toISOString(), reason });
  },
);

export default router;
