/**
 * GBP Multi-Location Service
 * ──────────────────────────
 * Location discovery, per-location metrics sync, review sync with
 * AI sentiment, multi-location comparison, and AI review replies.
 */

import { prisma } from "@/lib/prisma";
import { getValidAccessToken } from "./integration-credentials";
import { callLlm } from "@/lib/ai-avatar/llm-client";

const GBP_INFO_BASE = "https://mybusinessbusinessinformation.googleapis.com/v1";
const GBP_PERF_BASE = "https://businessprofileperformance.googleapis.com/v1";
const GBP_ACCOUNTS_URL = "https://mybusinessaccountmanagement.googleapis.com/v1/accounts";

async function gbpFetch(url: string, token: string): Promise<unknown> {
  const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
  if (!res.ok) { console.error(`[gbp] ${res.status} ${url}`); return null; }
  return res.json();
}

// ─── Location discovery ─────────────────────────────────────────────────────

export async function discoverLocations(brandId: string): Promise<{ discovered: number }> {
  const token = await getValidAccessToken(brandId, "gbp");

  // Step 1: List accounts
  const accountsData = await gbpFetch(GBP_ACCOUNTS_URL, token) as { accounts?: Array<{ name?: string }> } | null;
  const accounts = accountsData?.accounts ?? [];
  console.log("[gbp] Accounts found:", accounts.length);
  if (accounts.length === 0) return { discovered: 0 };

  // Step 2: List locations for each account
  let count = 0;
  for (const account of accounts) {
    if (!account.name) continue;
    const locUrl = `${GBP_INFO_BASE}/${account.name}/locations?readMask=name,title,storefrontAddress,phoneNumbers,websiteUri,primaryCategory,metadata&pageSize=100`;
    const data = await gbpFetch(locUrl, token) as { locations?: Array<Record<string, unknown>> } | null;
    if (!data?.locations) continue;

    for (const loc of data.locations) {
    const googleId = String(loc.name ?? "");
    const addr = loc.storefrontAddress as Record<string, unknown> | undefined;
    const phones = loc.phoneNumbers as { primaryPhone?: string } | undefined;
    const cat = loc.primaryCategory as { displayName?: string } | undefined;
    const meta = loc.metadata as { placeId?: string; mapsUri?: string; latlng?: { latitude?: number; longitude?: number } } | undefined;
    const lines = addr?.addressLines as string[] | undefined;

    await prisma.gbpLocation.upsert({
      where: { brandId_googleLocationId: { brandId, googleLocationId: googleId } },
      create: {
        brandId, googleLocationId: googleId,
        locationName: String(loc.title ?? googleId),
        address: lines?.join(", ") ?? null,
        city: (addr?.locality as string) ?? null,
        state: (addr?.administrativeArea as string) ?? null,
        zip: (addr?.postalCode as string) ?? null,
        phone: phones?.primaryPhone ?? null,
        website: (loc.websiteUri as string) ?? null,
        category: cat?.displayName ?? null,
        placeId: meta?.placeId ?? null,
        latitude: meta?.latlng?.latitude ?? null,
        longitude: meta?.latlng?.longitude ?? null,
      },
      update: {
        locationName: String(loc.title ?? googleId),
        phone: phones?.primaryPhone ?? undefined,
        website: (loc.websiteUri as string) ?? undefined,
        category: cat?.displayName ?? undefined,
      },
    });
    count++;
    }
  }
  return { discovered: count };
}

// ─── Metrics sync ───────────────────────────────────────────────────────────

export async function syncMetrics(locationId: string, brandId: string, days = 90): Promise<number> {
  const loc = await prisma.gbpLocation.findUnique({ where: { id: locationId } });
  if (!loc) return 0;
  const token = await getValidAccessToken(brandId, "gbp");
  const end = new Date();
  const start = new Date(end.getTime() - days * 86400000);

  const data = await gbpFetch(
    `${GBP_PERF_BASE}/${loc.googleLocationId}:fetchMultiDailyMetricsTimeSeries?dailyMetrics=BUSINESS_IMPRESSIONS_DESKTOP_MAPS&dailyMetrics=BUSINESS_IMPRESSIONS_MOBILE_MAPS&dailyMetrics=BUSINESS_IMPRESSIONS_DESKTOP_SEARCH&dailyMetrics=BUSINESS_IMPRESSIONS_MOBILE_SEARCH&dailyMetrics=WEBSITE_CLICKS&dailyMetrics=CALL_CLICKS&dailyMetrics=BUSINESS_DIRECTION_REQUESTS&dailyRange.startDate.year=${start.getUTCFullYear()}&dailyRange.startDate.month=${start.getUTCMonth() + 1}&dailyRange.startDate.day=${start.getUTCDate()}&dailyRange.endDate.year=${end.getUTCFullYear()}&dailyRange.endDate.month=${end.getUTCMonth() + 1}&dailyRange.endDate.day=${end.getUTCDate()}`,
    token,
  ) as { multiDailyMetricTimeSeries?: Array<{ dailyMetricTimeSeries?: { timeSeries?: { datedValues?: Array<{ date?: { year?: number; month?: number; day?: number }; value?: string }> } } }> } | null;

  if (!data?.multiDailyMetricTimeSeries) return 0;
  // Aggregate by date across all metric types
  const byDate = new Map<string, Record<string, number>>();
  const metricNames = ["viewsMaps", "viewsMaps2", "viewsSearch", "viewsSearch2", "websiteClicks", "phoneClicks", "directionClicks"];
  data.multiDailyMetricTimeSeries.forEach((series, idx) => {
    const values = series.dailyMetricTimeSeries?.timeSeries?.datedValues ?? [];
    for (const dv of values) {
      if (!dv.date) continue;
      const key = `${dv.date.year}-${String(dv.date.month).padStart(2, "0")}-${String(dv.date.day).padStart(2, "0")}`;
      if (!byDate.has(key)) byDate.set(key, {});
      const row = byDate.get(key)!;
      const name = metricNames[idx] ?? `metric_${idx}`;
      row[name] = (row[name] || 0) + Number(dv.value || 0);
    }
  });

  let stored = 0;
  for (const [dateStr, metrics] of byDate) {
    const date = new Date(dateStr + "T00:00:00Z");
    await prisma.gbpMetric.upsert({
      where: { locationId_date: { locationId, date } },
      create: {
        locationId, date,
        viewsMaps: (metrics.viewsMaps || 0) + (metrics.viewsMaps2 || 0),
        viewsSearch: (metrics.viewsSearch || 0) + (metrics.viewsSearch2 || 0),
        viewsTotal: (metrics.viewsMaps || 0) + (metrics.viewsMaps2 || 0) + (metrics.viewsSearch || 0) + (metrics.viewsSearch2 || 0),
        websiteClicks: metrics.websiteClicks || 0,
        phoneClicks: metrics.phoneClicks || 0,
        directionClicks: metrics.directionClicks || 0,
      },
      update: {
        viewsMaps: (metrics.viewsMaps || 0) + (metrics.viewsMaps2 || 0),
        viewsSearch: (metrics.viewsSearch || 0) + (metrics.viewsSearch2 || 0),
        viewsTotal: (metrics.viewsMaps || 0) + (metrics.viewsMaps2 || 0) + (metrics.viewsSearch || 0) + (metrics.viewsSearch2 || 0),
        websiteClicks: metrics.websiteClicks || 0,
        phoneClicks: metrics.phoneClicks || 0,
        directionClicks: metrics.directionClicks || 0,
      },
    });
    stored++;
  }
  return stored;
}

// ─── Review sync with AI sentiment ──────────────────────────────────────────

export async function syncReviews(locationId: string, brandId: string): Promise<number> {
  const loc = await prisma.gbpLocation.findUnique({ where: { id: locationId } });
  if (!loc) return 0;
  const token = await getValidAccessToken(brandId, "gbp");
  const data = await gbpFetch(`https://mybusiness.googleapis.com/v4/${loc.googleLocationId}/reviews`, token) as { reviews?: Array<Record<string, unknown>> } | null;
  if (!data?.reviews) return 0;

  let count = 0;
  for (const rev of data.reviews.slice(0, 100)) {
    const googleReviewId = String(rev.reviewId ?? rev.name ?? `${locationId}_${count}`);
    const reviewer = rev.reviewer as { displayName?: string } | undefined;
    const starRating = rev.starRating as string | undefined;
    const rating = starRating === "FIVE" ? 5 : starRating === "FOUR" ? 4 : starRating === "THREE" ? 3 : starRating === "TWO" ? 2 : 1;
    const comment = (rev.comment as string) ?? null;
    const reply = rev.reviewReply as { comment?: string; updateTime?: string } | undefined;

    // AI sentiment analysis (best effort)
    let sentiment: string | null = null;
    let topics: string[] = [];
    if (comment && comment.length > 10) {
      try {
        const ai = await callLlm([
          { role: "system", content: "Classify this review. Respond ONLY with JSON." },
          { role: "user", content: `Rating: ${rating}. Text: "${comment.slice(0, 500)}"\nReturn: {"sentiment":"positive|negative|neutral|mixed","topics":["topic1"]}` },
        ], () => JSON.stringify({ sentiment: rating >= 4 ? "positive" : rating <= 2 ? "negative" : "neutral", topics: [] }), { maxTokens: 200 });
        const parsed = JSON.parse(ai.reply.replace(/```json?\s*/gi, "").replace(/```/g, "").trim());
        sentiment = parsed.sentiment; topics = parsed.topics ?? [];
      } catch { sentiment = rating >= 4 ? "positive" : rating <= 2 ? "negative" : "neutral"; }
    }

    await prisma.gbpReview.upsert({
      where: { locationId_googleReviewId: { locationId, googleReviewId } },
      create: { locationId, googleReviewId, reviewerName: reviewer?.displayName ?? null, rating, comment, replyText: reply?.comment ?? null, repliedAt: reply?.updateTime ? new Date(reply.updateTime) : null, publishedAt: new Date(String(rev.createTime ?? rev.updateTime ?? new Date().toISOString())), sentiment, topics },
      update: { replyText: reply?.comment ?? undefined, repliedAt: reply?.updateTime ? new Date(reply.updateTime) : undefined, sentiment, topics },
    });
    count++;
  }

  // Update location aggregate
  const agg = await prisma.gbpReview.aggregate({ where: { locationId }, _avg: { rating: true }, _count: true });
  await prisma.gbpLocation.update({ where: { id: locationId }, data: { avgRating: agg._avg.rating ?? 0, totalReviews: agg._count } });
  return count;
}

// ─── AI review reply draft ──────────────────────────────────────────────────

export async function generateReplyDraft(reviewId: string, brandName: string): Promise<string> {
  const review = await prisma.gbpReview.findUnique({ where: { id: reviewId } });
  if (!review) return "";
  const result = await callLlm([
    { role: "system", content: "Write a professional, warm reply. Under 200 words." },
    { role: "user", content: `Reply to this review for ${brandName}.\nRating: ${review.rating}/5\nComment: "${review.comment || "(no text)"}"\nBe warm, address concerns, invite them back.` },
  ], () => `Thank you for your feedback! We appreciate you choosing ${brandName}.`, { maxTokens: 400 });
  return result.reply.trim();
}

// ─── Multi-location comparison ──────────────────────────────────────────────

export async function compareLocations(locationIds: string[], days = 30): Promise<Array<{ locationId: string; locationName: string; totals: Record<string, number> }>> {
  const since = new Date(Date.now() - days * 86400000);
  const results: Array<{ locationId: string; locationName: string; totals: Record<string, number> }> = [];

  for (const id of locationIds.slice(0, 50)) {
    const loc = await prisma.gbpLocation.findUnique({ where: { id }, select: { locationName: true } });
    const agg = await prisma.gbpMetric.aggregate({
      where: { locationId: id, date: { gte: since } },
      _sum: { viewsTotal: true, viewsMaps: true, viewsSearch: true, websiteClicks: true, phoneClicks: true, directionClicks: true },
    });
    results.push({
      locationId: id, locationName: loc?.locationName ?? id,
      totals: {
        views: agg._sum.viewsTotal ?? 0, viewsMaps: agg._sum.viewsMaps ?? 0, viewsSearch: agg._sum.viewsSearch ?? 0,
        websiteClicks: agg._sum.websiteClicks ?? 0, phoneClicks: agg._sum.phoneClicks ?? 0, directionClicks: agg._sum.directionClicks ?? 0,
      },
    });
  }
  return results.sort((a, b) => (b.totals.views ?? 0) - (a.totals.views ?? 0));
}
