/**
 * Google Search Console Integration
 * ------------------------------------
 * Auth: OAuth 2.0 (access token + refresh token)
 * API: https://www.googleapis.com/webmasters/v3
 *
 * On sync:
 *   1. Fetches query-level data (keywords + clicks + impressions + CTR + position)
 *   2. Fetches page-level data (landing pages + the same metrics)
 *   3. Stores everything via gsc-sync service into GscQuery + GscPage + MetricSnapshot
 */

import type { IntegrationCredentials } from "@/lib/services/integration-credentials";
import { SyncService, SyncResult, apiCall, apiCallAuthed } from "./types";
import { storeGscData } from "@/lib/services/gsc-sync";
import { prisma } from "@/lib/prisma";

const API_BASE = "https://www.googleapis.com/webmasters/v3";

/** Days to pull on first sync (backfill) — gives YoY-minus-a-year-ish window. */
const BACKFILL_DAYS = 90;
/** Days to pull on incremental sync — enough to catch GSC's 2-3 day processing delay. */
const INCREMENTAL_DAYS = 7;

export const googleSearchConsole: SyncService = {
  id: "gsc",
  name: "Google Search Console",

  async connect(brandId: string, creds?: IntegrationCredentials): Promise<SyncResult> {
    if (!creds?.accessToken) {
      return {
        success: false,
        message: "Google Search Console requires an OAuth access token. Complete the Google OAuth flow to connect.",
      };
    }

    // Verify the token works by listing sites
    const res = await apiCall(`${API_BASE}/sites`, {
      bearerToken: creds.accessToken,
    });

    if (!res.ok) {
      return { success: false, message: `GSC connection failed: ${res.error}` };
    }

    // Extract the list of verified sites so the user can pick one
    const sites = res.data as { siteEntry?: Array<{ siteUrl: string; permissionLevel: string }> };
    const siteList = sites.siteEntry?.map(s => ({
      url: s.siteUrl,
      permission: s.permissionLevel,
    })) ?? [];

    return {
      success: true,
      message: `Google Search Console connected — ${siteList.length} properties found.`,
      credentials: creds,
      data: { sites: siteList },
    };
  },

  async disconnect(): Promise<SyncResult> {
    return { success: true, message: "Google Search Console disconnected." };
  },

  async sync(brandId: string, creds?: IntegrationCredentials, options?: { force?: boolean }): Promise<SyncResult> {
    if (!creds?.accessToken) {
      return { success: false, message: "No access token — reconnect Google Search Console." };
    }

    const siteUrl = (creds.siteUrl as string) || (creds.accountId as string);
    if (!siteUrl) {
      return { success: false, message: "No site URL configured. Reconnect and select a property." };
    }

    // apiCallAuthed handles token refresh + one-shot 401 retry, so individual
    // call sites below don't need to worry about expired tokens.

    // Force full re-sync: wipe existing GSC rows for this brand so the sync
    // path treats it as a first-time backfill (90-day window).
    if (options?.force) {
      await prisma.metricSnapshot.deleteMany({ where: { brandId, source: "gsc" } });
      await prisma.gscQuery.deleteMany({ where: { brandId } });
      await prisma.gscPage.deleteMany({ where: { brandId } });
    }

    // Backfill (90 days) on first sync gives immediate trend/YoY data.
    // Incremental syncs (7 days) cover GSC's 2-3 day processing delay without
    // re-pulling the full history on every run. We can't use
    // brandIntegration.lastSynced here because the OAuth callback writes that
    // field on connect, before any data has actually been fetched — so
    // presence of snapshots is the accurate backfill signal.
    const existingSnapshots = await prisma.metricSnapshot.count({
      where: { brandId, source: "gsc" },
    });
    const isBackfill = existingSnapshots === 0;
    const daysBack = isBackfill ? BACKFILL_DAYS : INCREMENTAL_DAYS;

    const endDate = new Date().toISOString().split("T")[0];
    const startDate = new Date(Date.now() - daysBack * 86400000).toISOString().split("T")[0];
    const encodedSite = encodeURIComponent(siteUrl);

    // ── 1. Fetch per-day totals (one row per day) for MetricSnapshot ──
    let dailyRes: Awaited<ReturnType<typeof apiCallAuthed>>;
    try {
      dailyRes = await apiCallAuthed(
        `${API_BASE}/sites/${encodedSite}/searchAnalytics/query`,
        {
          method: "POST",
          body: { startDate, endDate, dimensions: ["date"], rowLimit: 25000 },
        },
        { brandId, integrationId: "gsc" },
      );
    } catch (err) {
      const msg = err instanceof Error ? err.message : "Reconnect Google Search Console";
      return { success: false, message: msg };
    }

    if (!dailyRes.ok) {
      if (dailyRes.status === 401) {
        return { success: false, message: "GSC refresh token revoked — reconnect Google Search Console." };
      }
      return { success: false, message: `GSC sync failed: ${dailyRes.error}` };
    }

    const dailyData = dailyRes.data as { rows?: Array<{ keys: string[]; clicks: number; impressions: number; ctr: number; position: number }> };
    const dailyRows = dailyData.rows ?? [];

    // ── 2. Fetch per-day per-page data with pagination for daily GscPage rows ──
    type GscRow = { keys: string[]; clicks: number; impressions: number; ctr: number; position: number };
    let dailyPageRows: GscRow[] = [];
    {
      const ROW_LIMIT = 25000;
      let startRow = 0;
      let calls = 0;
      while (startRow <= 200000) {
        const res = await apiCallAuthed(
          `${API_BASE}/sites/${encodedSite}/searchAnalytics/query`,
          {
            method: "POST",
            body: { startDate, endDate, dimensions: ["date", "page"], rowLimit: ROW_LIMIT, startRow },
          },
          { brandId, integrationId: "gsc" },
        );
        calls++;
        if (!res.ok) break;
        const rows = ((res.data as { rows?: GscRow[] }).rows ?? []);
        dailyPageRows = dailyPageRows.concat(rows);
        if (rows.length < ROW_LIMIT) break;
        startRow += ROW_LIMIT;
      }
      const uniqueDates = new Set(dailyPageRows.map(r => r.keys[0])).size;
      console.info(`[gsc-sync] Daily page data: ${dailyPageRows.length} rows across ${uniqueDates} dates (${calls} API call${calls !== 1 ? "s" : ""})`);
    }

    // ── 3. Fetch query + page dimensions (aggregated) for GscQuery ──
    const queryRes = await apiCallAuthed(
      `${API_BASE}/sites/${encodedSite}/searchAnalytics/query`,
      {
        method: "POST",
        body: { startDate, endDate, dimensions: ["query", "page"], rowLimit: 5000 },
      },
      { brandId, integrationId: "gsc" },
    );

    if (!queryRes.ok) {
      if (queryRes.status === 401) {
        return { success: false, message: "GSC refresh token revoked — reconnect Google Search Console." };
      }
      return { success: false, message: `GSC sync failed: ${queryRes.error}` };
    }

    const queryData = queryRes.data as { rows?: Array<{ keys: string[]; clicks: number; impressions: number; ctr: number; position: number }> };
    const rows = queryData.rows ?? [];

    // ── Store everything ──
    const stored = await storeGscData(brandId, rows, startDate, endDate, dailyRows, dailyPageRows);

    // ── Summary for the response ──
    const totalClicks = dailyRows.reduce((s, r) => s + r.clicks, 0);
    const totalImpressions = dailyRows.reduce((s, r) => s + r.impressions, 0);

    return {
      success: true,
      message: `${isBackfill ? "Backfilled" : "Synced"} ${dailyRows.length} days of data (${daysBack}-day window): ${totalClicks.toLocaleString()} clicks, ${totalImpressions.toLocaleString()} impressions across ${rows.length} query/page combinations. Stored ${stored.snapshotsStored} daily snapshots, ${stored.queriesStored} queries, ${stored.pagesStored} pages.`,
      data: {
        totalClicks,
        totalImpressions,
        snapshotsStored: stored.snapshotsStored,
        queriesStored: stored.queriesStored,
        pagesStored: stored.pagesStored,
        period: { startDate, endDate },
      },
    };
  },
};
