/**
 * Google Business Profile Integration
 * --------------------------------------
 * Auth: OAuth 2.0
 * APIs:
 *   - Account management: mybusinessaccountmanagement.googleapis.com
 *   - Business info: mybusinessbusinessinformation.googleapis.com
 *   - Reviews: mybusiness.googleapis.com (v4)
 *   - Performance: businessprofileperformance.googleapis.com
 *
 * On sync:
 *   1. Fetches location details (name, address, category, rating)
 *   2. Fetches reviews
 *   3. Fetches performance metrics (searches, views, actions)
 *   4. Stores everything via gbp-sync service
 */

import type { IntegrationCredentials } from "@/lib/services/integration-credentials";
import { SyncService, SyncResult, apiCall, apiCallAuthed } from "./types";
import { storeGbpLocation, storeGbpReviews, storeGbpMetrics } from "@/lib/services/gbp-sync";

export const googleBusinessProfile: SyncService = {
  id: "gbp",
  name: "Google Business Profile",

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

    // List accounts to verify access
    const accountsRes = await apiCall(
      "https://mybusinessaccountmanagement.googleapis.com/v1/accounts",
      { bearerToken: creds.accessToken }
    );

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

    const accounts = accountsRes.data as { accounts?: Array<{ name: string; accountName: string }> };
    const accountList = accounts.accounts?.map(a => ({
      id: a.name,
      name: a.accountName,
    })) ?? [];

    return {
      success: true,
      message: `Google Business Profile connected — ${accountList.length} account(s) found.`,
      credentials: creds,
      data: { accounts: accountList },
    };
  },

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

  async sync(brandId: string, creds?: IntegrationCredentials): Promise<SyncResult> {
    if (!creds?.accessToken) {
      return { success: false, message: "No credentials — reconnect GBP." };
    }

    const accountId = creds.accountId as string;
    const locationId = creds.locationId as string;
    if (!accountId || !locationId) {
      return { success: false, message: "No account/location configured. Reconnect and select a location." };
    }

    // apiCallAuthed owns token refresh + 401 retry.
    const auth = { brandId, integrationId: "gbp" };
    let reviewsStored = 0;
    let metricsStored = 0;

    // ── 1. Fetch location details ──
    let locationRes: Awaited<ReturnType<typeof apiCallAuthed>>;
    try {
      locationRes = await apiCallAuthed(
        `https://mybusinessbusinessinformation.googleapis.com/v1/${locationId}?readMask=name,title,storefrontAddress,phoneNumbers,categories,websiteUri,metadata`,
        {},
        auth,
      );
    } catch (err) {
      const msg = err instanceof Error ? err.message : "Reconnect Google Business Profile";
      return { success: false, message: msg };
    }

    if (locationRes.ok) {
      const loc = locationRes.data as Record<string, unknown>;
      const addr = loc.storefrontAddress as Record<string, unknown> | undefined;
      const cats = loc.categories as { primaryCategory?: { displayName?: string } } | undefined;
      const meta = loc.metadata as { mapsUri?: string; placeId?: string } | undefined;

      await storeGbpLocation(brandId, {
        locationId,
        locationName: (loc.title as string) ?? "Unknown",
        address: addr ? [addr.addressLines, addr.locality, addr.administrativeArea].filter(Boolean).join(", ") : undefined,
        phone: ((loc.phoneNumbers as Record<string, unknown>)?.primaryPhone as string) ?? undefined,
        category: cats?.primaryCategory?.displayName ?? undefined,
        websiteUrl: (loc.websiteUri as string) ?? undefined,
        mapsUrl: meta?.mapsUri ?? undefined,
        placeId: meta?.placeId ?? undefined,
      });
    }

    // ── 2. Fetch reviews ──
    const reviewsRes = await apiCallAuthed(
      `https://mybusiness.googleapis.com/v4/${accountId}/${locationId}/reviews?pageSize=50`,
      {},
      auth,
    );

    if (reviewsRes.ok) {
      const reviewData = reviewsRes.data as { reviews?: Array<Record<string, unknown>> };
      const reviews = (reviewData.reviews ?? []).map(r => {
        const reviewer = r.reviewer as Record<string, unknown> | undefined;
        return {
          reviewId: (r.reviewId as string) ?? (r.name as string) ?? `gbp-${Date.now()}-${Math.random()}`,
          authorName: (reviewer?.displayName as string) ?? "Anonymous",
          rating: starToNumber((r.starRating as string) ?? "FIVE"),
          comment: (r.comment as string) ?? undefined,
          replyText: ((r.reviewReply as Record<string, unknown>)?.comment as string) ?? undefined,
          publishedAt: (r.createTime as string) ?? new Date().toISOString(),
        };
      });

      reviewsStored = await storeGbpReviews(brandId, reviews);
    }

    // ── 3. Fetch performance metrics (last 28 days) ──
    // The Business Profile Performance API returns daily metric data
    const endDate = new Date().toISOString().split("T")[0];
    const startDate = new Date(Date.now() - 28 * 86400000).toISOString().split("T")[0];

    const perfRes = await apiCallAuthed(
      `https://businessprofileperformance.googleapis.com/v1/${locationId}:getDailyMetricsTimeSeries?dailyMetric=WEBSITE_CLICKS&dailyMetric=CALL_CLICKS&dailyMetric=BUSINESS_DIRECTION_REQUESTS&dailyMetric=BUSINESS_IMPRESSIONS_DESKTOP_MAPS&dailyMetric=BUSINESS_IMPRESSIONS_DESKTOP_SEARCH&dailyRange.startDate.year=${startDate.split("-")[0]}&dailyRange.startDate.month=${parseInt(startDate.split("-")[1])}&dailyRange.startDate.day=${parseInt(startDate.split("-")[2])}&dailyRange.endDate.year=${endDate.split("-")[0]}&dailyRange.endDate.month=${parseInt(endDate.split("-")[1])}&dailyRange.endDate.day=${parseInt(endDate.split("-")[2])}`,
      {},
      auth,
    );

    if (perfRes.ok) {
      // Parse the time series into daily metric rows
      const series = perfRes.data as { timeSeries?: Array<{ dailyMetric: string; timeSeries?: { datedValues?: Array<{ date: { year: number; month: number; day: number }; value?: string }> } }> };
      const dailyMap = new Map<string, Record<string, number>>();

      for (const ts of series.timeSeries ?? []) {
        for (const dv of ts.timeSeries?.datedValues ?? []) {
          const dateStr = `${dv.date.year}-${String(dv.date.month).padStart(2, "0")}-${String(dv.date.day).padStart(2, "0")}`;
          const existing = dailyMap.get(dateStr) ?? {};
          const val = parseInt(dv.value ?? "0", 10);

          switch (ts.dailyMetric) {
            case "WEBSITE_CLICKS": existing.actionsWebsite = val; break;
            case "CALL_CLICKS": existing.actionsPhone = val; break;
            case "BUSINESS_DIRECTION_REQUESTS": existing.actionsDirections = val; break;
            case "BUSINESS_IMPRESSIONS_DESKTOP_MAPS": existing.viewsMaps = (existing.viewsMaps ?? 0) + val; break;
            case "BUSINESS_IMPRESSIONS_DESKTOP_SEARCH": existing.viewsSearch = (existing.viewsSearch ?? 0) + val; break;
          }
          existing.viewsTotal = (existing.viewsMaps ?? 0) + (existing.viewsSearch ?? 0);
          dailyMap.set(dateStr, existing);
        }
      }

      const metricRows = Array.from(dailyMap.entries()).map(([date, m]) => ({
        date,
        actionsWebsite: m.actionsWebsite ?? 0,
        actionsPhone: m.actionsPhone ?? 0,
        actionsDirections: m.actionsDirections ?? 0,
        viewsMaps: m.viewsMaps ?? 0,
        viewsSearch: m.viewsSearch ?? 0,
        viewsTotal: m.viewsTotal ?? 0,
      }));

      metricsStored = await storeGbpMetrics(brandId, metricRows);
    }

    return {
      success: true,
      message: `GBP synced: ${reviewsStored} reviews, ${metricsStored} days of metrics.`,
      data: { reviewsStored, metricsStored },
    };
  },
};

// ── Helper ──

function starToNumber(star: string): number {
  const map: Record<string, number> = { ONE: 1, TWO: 2, THREE: 3, FOUR: 4, FIVE: 5 };
  return map[star] ?? 5;
}
