/**
 * GET /api/cron/refresh-tokens — Proactive Google OAuth token refresh.
 *
 * Runs every 45 minutes via Vercel Cron. Google access tokens live 60
 * minutes, so refreshing anything expiring within the next 20 minutes
 * keeps every connected brand's token valid around the clock — even if
 * no human visits the app. Without this, an integration that sat idle
 * for an hour would force the user's next visit to watch a 0-sessions
 * dashboard while the on-demand refresh kicked in.
 *
 * Protected by CRON_SECRET — same pattern as /api/cron/rank-check:
 * either Authorization: Bearer <secret> or ?secret=<secret>.
 *
 * Failure handling is delegated to refreshGoogleAccessToken, which now
 * tolerates two consecutive blips before marking an integration as
 * "expired" (see integration-credentials.ts).
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { forceRefreshAccessToken } from "@/lib/services/integration-credentials";
import { log } from "@/lib/logger";

export const dynamic = "force-dynamic";
export const maxDuration = 60;

const CRON_SECRET = process.env.CRON_SECRET ?? "";

function authOk(req: Request): boolean {
  if (!CRON_SECRET) return false;
  const header = req.headers.get("authorization") ?? "";
  if (header === `Bearer ${CRON_SECRET}`) return true;
  const url = new URL(req.url);
  return url.searchParams.get("secret") === CRON_SECRET;
}

// Google integrations this cron knows how to refresh. Keep in sync with
// the integration catalog — non-Google providers (bing, claude) handle
// their own auth and don't use the refresh_token flow.
const GOOGLE_INTEGRATION_IDS = ["ga4", "gsc", "gbp"] as const;

export async function GET(req: Request) {
  if (!authOk(req)) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  // 20-minute look-ahead: refresh anything that will expire before the
  // next cron tick (which runs every 45 minutes). Also refresh anything
  // whose expiry is already null / in the past so we self-heal previously
  // stuck rows.
  const cutoff = new Date(Date.now() + 20 * 60 * 1000);
  const candidates = await prisma.brandIntegration.findMany({
    where: {
      integrationId: { in: [...GOOGLE_INTEGRATION_IDS] },
      connected: true,
      // Pick up "expired" rows too — the refresh-success path will lift
      // them back to "connected" if Google accepts the refresh token.
      status: { in: ["connected", "error", "expired"] },
      OR: [
        { tokenExpiresAt: null },
        { tokenExpiresAt: { lte: cutoff } },
      ],
    },
    select: { id: true, brandId: true, integrationId: true, tokenExpiresAt: true, status: true, refreshFailCount: true },
  });

  const results: Array<{ brandId: string; integrationId: string; ok: boolean; error?: string }> = [];
  let refreshed = 0;
  let skipped = 0;
  let failed = 0;

  for (const integration of candidates) {
    try {
      await forceRefreshAccessToken(integration.brandId, integration.integrationId);
      refreshed++;
      results.push({ brandId: integration.brandId, integrationId: integration.integrationId, ok: true });
      console.info(`[cron/refresh-tokens] refreshed ${integration.integrationId} for brand ${integration.brandId}`);
    } catch (err) {
      const msg = err instanceof Error ? err.message : "unknown";
      // refreshGoogleAccessToken already handles the 3-strike expired-state
      // transition and persists the failure count; we just surface the
      // error here for visibility.
      if (/No stored credentials|No refresh_token/.test(msg)) {
        skipped++;
        results.push({ brandId: integration.brandId, integrationId: integration.integrationId, ok: false, error: "no_credentials" });
      } else if (msg.startsWith("[integration:expired]")) {
        skipped++;
        results.push({ brandId: integration.brandId, integrationId: integration.integrationId, ok: false, error: "integration_expired" });
      } else {
        failed++;
        results.push({ brandId: integration.brandId, integrationId: integration.integrationId, ok: false, error: msg });
        console.error(`[cron/refresh-tokens] failed ${integration.integrationId} for brand ${integration.brandId}: ${msg}`);
      }
    }
  }

  log.info("integration", `[cron/refresh-tokens] ${refreshed}/${candidates.length} refreshed, ${failed} failed, ${skipped} skipped`, {
    metadata: { candidates: candidates.length, refreshed, failed, skipped },
  });

  return NextResponse.json({
    candidates: candidates.length,
    refreshed,
    failed,
    skipped,
    results,
  });
}
