import crypto from "crypto";

/* Google Analytics Data API (GA4) client — minimal service-account flow
   implemented on node:crypto so no extra dependency is needed:
   sign a RS256 JWT → exchange for an access token → runReport. */

export interface AnalyticsCredentials {
  propertyId: string | null;
  clientEmail: string | null;
  privateKey: string | null;
}

interface TokenCache {
  token: string;
  expiresAt: number;
}

let tokenCache: TokenCache | null = null;

function normalizeKey(key: string): string {
  // keys pasted into a textarea often carry literal "\n" escapes
  return key.replace(/\\n/g, "\n").trim();
}

async function getAccessToken(credentials: AnalyticsCredentials): Promise<string> {
  if (tokenCache && tokenCache.expiresAt > Date.now() + 60_000) return tokenCache.token;

  const iat = Math.floor(Date.now() / 1000);
  const header = { alg: "RS256", typ: "JWT" };
  const claim = {
    iss: credentials.clientEmail,
    scope: "https://www.googleapis.com/auth/analytics.readonly",
    aud: "https://oauth2.googleapis.com/token",
    exp: iat + 3600,
    iat,
  };
  const b64 = (obj: unknown) => Buffer.from(JSON.stringify(obj)).toString("base64url");
  const unsigned = `${b64(header)}.${b64(claim)}`;
  let signature: string;
  try {
    signature = crypto
      .createSign("RSA-SHA256")
      .update(unsigned)
      .sign(normalizeKey(credentials.privateKey ?? ""))
      .toString("base64url");
  } catch {
    throw new Error("GA_PRIVATE_KEY_INVALID");
  }
  const assertion = `${unsigned}.${signature}`;

  const res = await fetch("https://oauth2.googleapis.com/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
      assertion,
    }),
    signal: AbortSignal.timeout(15_000),
  });
  const data = (await res.json()) as { access_token?: string; expires_in?: number; error_description?: string; error?: string };
  if (!res.ok || !data.access_token) {
    throw new Error(data.error_description || data.error || "GA_AUTH_FAILED");
  }
  tokenCache = {
    token: data.access_token,
    expiresAt: Date.now() + ((data.expires_in ?? 3600) - 120) * 1000,
  };
  return data.access_token;
}

async function runReport(
  credentials: AnalyticsCredentials,
  body: Record<string, unknown>
): Promise<{
  rows?: Array<{ dimensionValues?: Array<{ value: string }>; metricValues?: Array<{ value: string }> }>;
  totals?: Array<{ metricValues?: Array<{ value: string }> }>;
}> {
  const token = await getAccessToken(credentials);
  const res = await fetch(
    `https://analyticsdata.googleapis.com/v1beta/properties/${credentials.propertyId}:runReport`,
    {
      method: "POST",
      headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(20_000),
    }
  );
  const data = await res.json();
  if (!res.ok) {
    throw new Error((data as { error?: { message?: string } }).error?.message || `GA_API_${res.status}`);
  }
  return data;
}

export interface AnalyticsOverview {
  activeUsers: number;
  sessions: number;
  pageViews: number;
  avgSessionDurationSec: number;
  bounceRatePct: number;
  topPages: Array<{ path: string; views: number }>;
  topCountries: Array<{ country: string; users: number }>;
}

function num(v: string | undefined): number {
  const n = Number(v ?? 0);
  return Number.isFinite(n) ? n : 0;
}

/** Last-28-days overview: headline KPIs + top pages + top countries. */
export async function fetchAnalyticsOverview(
  credentials: AnalyticsCredentials
): Promise<AnalyticsOverview> {
  const common = { dateRanges: [{ startDate: "28daysAgo", endDate: "today" }], limit: 10 };

  const [kpis, pages, countries] = await Promise.all([
    runReport(credentials, {
      ...common,
      metrics: [
        { name: "activeUsers" },
        { name: "sessions" },
        { name: "screenPageViews" },
        { name: "bounceRate" },
        { name: "averageSessionDuration" },
      ],
    }),
    runReport(credentials, {
      ...common,
      dimensions: [{ name: "pagePath" }],
      metrics: [{ name: "screenPageViews" }],
      orderBys: [{ metric: { metricName: "screenPageViews" }, desc: true }],
    }),
    runReport(credentials, {
      ...common,
      dimensions: [{ name: "country" }],
      metrics: [{ name: "activeUsers" }],
      orderBys: [{ metric: { metricName: "activeUsers" }, desc: true }],
    }),
  ]);

  const totals = kpis.totals?.[0]?.metricValues?.map((m) => m.value) ?? [];

  return {
    activeUsers: num(totals[0]),
    sessions: num(totals[1]),
    pageViews: num(totals[2]),
    bounceRatePct: Math.round(num(totals[3]) * 100),
    avgSessionDurationSec: Math.round(num(totals[4])),
    topPages: (pages.rows ?? []).slice(0, 5).map((row) => ({
      path: row.dimensionValues?.[0]?.value ?? "/",
      views: num(row.metricValues?.[0]?.value),
    })),
    topCountries: (countries.rows ?? []).slice(0, 5).map((row) => ({
      country: row.dimensionValues?.[0]?.value ?? "—",
      users: num(row.metricValues?.[0]?.value),
    })),
  };
}

export function isAnalyticsConfigured(c: AnalyticsCredentials): boolean {
  return Boolean(c.propertyId && c.clientEmail && c.privateKey);
}
