/**
 * GET /api/admin/debug/ga4-token?brandId=<id>
 *
 * Diagnostic endpoint for the GA4 invalid_grant / post-reconnect failure.
 *
 * Surfaces:
 *   - Full BrandIntegration row state (all auth-relevant fields)
 *   - Whether the cron's WHERE clause would pick this row up RIGHT NOW
 *   - Whether credentials can be decrypted + whether a refresh_token is stored
 *   - Which GOOGLE_CLIENT_ID the server is using (env vs hardcoded fallback)
 *   - Whether GOOGLE_CLIENT_SECRET is set
 *   - Plain-English diagnosis
 *
 * Superadmin-only. Never logs or returns actual token values.
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireSuperadmin } from "@/lib/admin-auth";
import { decryptCredentials } from "@/lib/services/integration-credentials";

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

// Read from env — do not hardcode credentials in source.
const OAUTH_INITIATION_FALLBACK_CLIENT_ID = process.env.GOOGLE_CLIENT_ID || "";

export async function GET(req: Request) {
  const denied = await requireSuperadmin();
  if (denied) return denied;

  const { searchParams } = new URL(req.url);
  const brandId = searchParams.get("brandId");
  if (!brandId) return NextResponse.json({ error: "brandId required" }, { status: 400 });

  const brand = await prisma.brand.findUnique({
    where: { id: brandId },
    select: { id: true, name: true, domain: true, createdAt: true },
  });
  if (!brand) return NextResponse.json({ error: "Brand not found" }, { status: 404 });

  const row = await prisma.brandIntegration.findUnique({
    where: { brandId_integrationId: { brandId, integrationId: "ga4" } },
  });

  if (!row) {
    return NextResponse.json({
      brand: { id: brand.id, name: brand.name },
      integration: null,
      diagnosis: "No BrandIntegration row for ga4 — integration was never connected.",
    });
  }

  // ── Credential inspection ──────────────────────────────────────────────────
  let credentialState: "missing" | "decrypt_failed" | "no_refresh_token" | "has_refresh_token" = "missing";
  if (row.credentialsEnc) {
    const creds = decryptCredentials(row.credentialsEnc);
    if (!creds) {
      credentialState = "decrypt_failed";
    } else if (!creds.refreshToken) {
      credentialState = "no_refresh_token";
    } else {
      credentialState = "has_refresh_token";
    }
  }

  // ── Cron eligibility simulation ────────────────────────────────────────────
  // Mirrors the WHERE clause in /api/cron/refresh-tokens/route.ts exactly.
  const cutoff = new Date(Date.now() + 20 * 60 * 1000); // now + 20 min
  const cronStatuses = ["connected", "error", "expired"];
  const cronEligible =
    row.connected === true &&
    cronStatuses.includes(row.status) &&
    (row.tokenExpiresAt === null || row.tokenExpiresAt <= cutoff);

  const tokenExpiresInMin = row.tokenExpiresAt
    ? Math.round((row.tokenExpiresAt.getTime() - Date.now()) / 60000)
    : null;

  // ── Google OAuth environment ──────────────────────────────────────────────
  const envClientId = process.env.GOOGLE_CLIENT_ID ?? null;
  const envClientSecret = process.env.GOOGLE_CLIENT_SECRET ?? null;
  const clientIdMatchesFallback = envClientId === OAUTH_INITIATION_FALLBACK_CLIENT_ID;

  // ── Diagnosis ──────────────────────────────────────────────────────────────
  const diagnoses: string[] = [];

  if (!row.connected) {
    diagnoses.push("BLOCKED: connected=false — cron requires connected=true, this row is skipped entirely.");
  }

  if (!cronStatuses.includes(row.status)) {
    diagnoses.push(`BLOCKED: status="${row.status}" is not in cron's allowed set [connected, error, expired].`);
  }

  if (row.tokenExpiresAt && row.tokenExpiresAt > cutoff) {
    diagnoses.push(`INFO: tokenExpiresAt is ${tokenExpiresInMin}m in the future — cron will skip this row until within 20 min of expiry. The stored access_token is still valid.`);
  }

  if (credentialState === "missing") {
    diagnoses.push("BUG: credentialsEnc is NULL — reconnect did not persist credentials (or disconnect wiped them and reconnect failed). Cron will throw 'No stored credentials' and skips.");
  } else if (credentialState === "decrypt_failed") {
    diagnoses.push("BUG: credentialsEnc is set but decryption failed — INTEGRATION_ENCRYPTION_KEY may have changed since credentials were stored. Cron gets null creds → 'No stored credentials'.");
  } else if (credentialState === "no_refresh_token") {
    diagnoses.push("BUG: credentials decrypted OK but no refresh_token stored. This happens when Google doesn't return a refresh_token on OAuth (only returns one on first grant or with prompt=consent). Cron throws 'No refresh_token stored' which is logged as 'skipped/no_credentials', not invalid_grant.");
  } else {
    diagnoses.push("INFO: credentials decrypted OK and refresh_token IS present.");
  }

  if (row.refreshFailCount && row.refreshFailCount >= 3) {
    diagnoses.push(`WARNING: refreshFailCount=${row.refreshFailCount} — at or above the 3-strike threshold. Reconnect (OAuth callback) does NOT reset refreshFailCount. Next invalid_grant will NOT increment it further (already at/above threshold) but status was already set to 'expired' on the 3rd failure. Status is now '${row.status}'.`);
  }

  if (!envClientId) {
    diagnoses.push("WARNING: GOOGLE_CLIENT_ID env var is not set on this server. OAuth initiation uses hardcoded fallback; cron refresh uses undefined → throws 'not configured'.");
  } else if (!clientIdMatchesFallback) {
    diagnoses.push(`WARNING: GOOGLE_CLIENT_ID env var (${envClientId.slice(0, 20)}...) differs from hardcoded fallback in /api/auth/google. If the initiation route uses the fallback and cron uses the env var, they may mismatch → tokens issued for one client_id can't be refreshed by another → invalid_grant.`);
  } else {
    diagnoses.push("INFO: GOOGLE_CLIENT_ID env matches the hardcoded fallback in /api/auth/google — no client_id mismatch.");
  }

  if (!envClientSecret) {
    diagnoses.push("BUG: GOOGLE_CLIENT_SECRET is not set — cron refresh throws 'not configured' immediately.");
  } else {
    diagnoses.push("INFO: GOOGLE_CLIENT_SECRET is set (value not shown).");
  }

  const updatedMsAgo = Date.now() - row.updatedAt.getTime();
  const updatedMinAgo = Math.round(updatedMsAgo / 60000);

  return NextResponse.json({
    brand: { id: brand.id, name: brand.name, domain: brand.domain },
    integration: {
      id: row.id,
      brandId: row.brandId,
      integrationId: row.integrationId,
      connected: row.connected,
      status: row.status,
      syncError: row.syncError,
      syncRetryCount: row.syncRetryCount,
      refreshFailCount: (row as Record<string, unknown>).refreshFailCount ?? "field_not_in_select",
      tokenExpiresAt: row.tokenExpiresAt?.toISOString() ?? null,
      tokenExpiresInMin,
      lastSynced: row.lastSynced?.toISOString() ?? null,
      nextRetryAt: row.nextRetryAt?.toISOString() ?? null,
      credentialsPresent: !!row.credentialsEnc,
      credentialLength: row.credentialsEnc?.length ?? 0,
      credentialState,
      createdAt: row.createdAt.toISOString(),
      updatedAt: row.updatedAt.toISOString(),
      updatedMinAgo,
    },
    cronEligibility: {
      wouldBePickedUpNow: cronEligible,
      requiredConnected: true,
      actualConnected: row.connected,
      requiredStatuses: cronStatuses,
      actualStatus: row.status,
      cutoffTime: cutoff.toISOString(),
      tokenExpiresAt: row.tokenExpiresAt?.toISOString() ?? null,
      tokenExpiresInMin,
      reason: cronEligible
        ? "Row would be included in next cron run"
        : `Row would be SKIPPED — ${!row.connected ? "connected=false" : !cronStatuses.includes(row.status) ? `status="${row.status}" not in allowed set` : `tokenExpiresAt is ${tokenExpiresInMin}m away (> 20m cutoff)`}`,
    },
    oauthEnvironment: {
      envClientIdPresent: !!envClientId,
      envClientIdPrefix: envClientId ? envClientId.slice(0, 30) + "..." : null,
      hardcodedFallbackClientId: OAUTH_INITIATION_FALLBACK_CLIENT_ID,
      clientIdMatchesFallback,
      envClientSecretPresent: !!envClientSecret,
      note: "OAuth initiation (/api/auth/google) uses env var || hardcoded fallback. Cron refresh uses env var only (no fallback). Mismatch → invalid_grant.",
    },
    diagnoses,
  });
}
