import { unstable_noStore as noStore } from "next/cache";
import { SITE } from "./constants";
import { prisma } from "./prisma";

export type GoogleReview = {
  id: string;
  authorName: string;
  authorUri: string | null;
  authorPhotoUri: string | null;
  rating: number;
  text: string;
  relativeTime: string;
  googleMapsUri: string | null;
};

export type GoogleReviewsSummary = {
  rating: number | null;
  reviewCount: number | null;
  googleMapsUri: string;
  reviews: GoogleReview[];
};

type LocalizedText = {
  text?: string;
  languageCode?: string;
};

type PlacesReview = {
  name?: string;
  rating?: number;
  relativePublishTimeDescription?: string;
  text?: LocalizedText;
  originalText?: LocalizedText;
  authorAttribution?: {
    displayName?: string;
    uri?: string;
    photoUri?: string;
  };
  googleMapsUri?: string;
};

type PlacesDetailsResponse = {
  rating?: number;
  userRatingCount?: number;
  googleMapsUri?: string;
  reviews?: PlacesReview[];
};

const META_ID = "default";
const SYNC_MAX_AGE_MS = 60 * 60 * 1000;

function googleMapsPlaceUrl(placeId: string) {
  return `https://www.google.com/maps/search/?api=1&query=Google&query_place_id=${encodeURIComponent(placeId)}`;
}

function pickReviewText(review: PlacesReview): string | undefined {
  const original = review.originalText?.text?.trim();
  const localized = review.text?.text?.trim();
  return original || localized;
}

function parseReview(review: PlacesReview): GoogleReview | null {
  const text = pickReviewText(review);
  const authorName = review.authorAttribution?.displayName?.trim();
  if (!text || !authorName || !review.rating) return null;

  return {
    id: review.name ?? `${authorName}-${review.relativePublishTimeDescription ?? ""}`,
    authorName,
    authorUri: review.authorAttribution?.uri ?? null,
    authorPhotoUri: review.authorAttribution?.photoUri ?? null,
    rating: review.rating,
    text,
    relativeTime: review.relativePublishTimeDescription ?? "",
    googleMapsUri: review.googleMapsUri ?? null,
  };
}

function toSummary(
  meta: {
    rating: number | null;
    reviewCount: number | null;
    googleMapsUri: string;
  },
  reviews: GoogleReview[]
): GoogleReviewsSummary | null {
  if (reviews.length === 0) return null;
  return {
    rating: meta.rating,
    reviewCount: meta.reviewCount,
    googleMapsUri: meta.googleMapsUri,
    reviews,
  };
}

async function readReviewsFromDb(): Promise<GoogleReviewsSummary | null> {
  const [meta, rows] = await Promise.all([
    prisma.googleReviewsMeta.findUnique({ where: { id: META_ID } }),
    prisma.googleReview.findMany({ orderBy: { sortOrder: "asc" } }),
  ]);

  if (!meta || rows.length === 0) return null;

  const reviews: GoogleReview[] = rows.map((row) => ({
    id: row.id,
    authorName: row.authorName,
    authorUri: row.authorUri,
    authorPhotoUri: row.authorPhotoUri,
    rating: row.rating,
    text: row.text,
    relativeTime: row.relativeTime,
    googleMapsUri: row.googleMapsUri,
  }));

  return toSummary(meta, reviews);
}

async function saveReviewsToDb(
  placeId: string,
  data: {
    rating: number | null;
    reviewCount: number | null;
    googleMapsUri: string;
    reviews: GoogleReview[];
  }
) {
  const [meta, existing] = await Promise.all([
    prisma.googleReviewsMeta.findUnique({ where: { id: META_ID } }),
    prisma.googleReview.findMany({ select: { id: true, sortOrder: true } }),
  ]);

  const placeChanged = meta != null && meta.placeId !== placeId;
  const sortOrderById = new Map(
    (placeChanged ? [] : existing).map((row) => [row.id, row.sortOrder])
  );
  let nextSortOrder =
    (placeChanged ? [] : existing).reduce(
      (max, row) => Math.max(max, row.sortOrder),
      -1
    ) + 1;

  await prisma.$transaction([
    prisma.googleReviewsMeta.upsert({
      where: { id: META_ID },
      create: {
        id: META_ID,
        placeId,
        rating: data.rating,
        reviewCount: data.reviewCount,
        googleMapsUri: data.googleMapsUri,
      },
      update: {
        placeId,
        rating: data.rating,
        reviewCount: data.reviewCount,
        googleMapsUri: data.googleMapsUri,
      },
    }),
    ...(placeChanged ? [prisma.googleReview.deleteMany({})] : []),
    ...data.reviews.map((review) => {
      const sortOrder = sortOrderById.get(review.id) ?? nextSortOrder++;
      return prisma.googleReview.upsert({
        where: { id: review.id },
        create: {
          id: review.id,
          authorName: review.authorName,
          authorUri: review.authorUri,
          authorPhotoUri: review.authorPhotoUri,
          rating: review.rating,
          text: review.text,
          relativeTime: review.relativeTime,
          googleMapsUri: review.googleMapsUri,
          sortOrder,
        },
        update: {
          authorName: review.authorName,
          authorUri: review.authorUri,
          authorPhotoUri: review.authorPhotoUri,
          rating: review.rating,
          text: review.text,
          relativeTime: review.relativeTime,
          googleMapsUri: review.googleMapsUri,
        },
      });
    }),
  ]);
}

async function fetchReviewsFromGoogle(
  apiKey: string,
  placeId: string
): Promise<GoogleReviewsSummary | null> {
  const url = new URL(
    `https://places.googleapis.com/v1/places/${encodeURIComponent(placeId)}`
  );
  url.searchParams.set("languageCode", "fr");
  url.searchParams.set("regionCode", "FR");
  const fallbackMapsUri = googleMapsPlaceUrl(placeId);

  const res = await fetch(url.toString(), {
    headers: {
      "Content-Type": "application/json",
      "X-Goog-Api-Key": apiKey,
      "X-Goog-FieldMask": "reviews,rating,userRatingCount,googleMapsUri",
    },
  });

  if (!res.ok) {
    console.error("Google Places API error:", res.status, await res.text());
    return null;
  }

  const data = (await res.json()) as PlacesDetailsResponse;
  const reviews = (data.reviews ?? [])
    .map(parseReview)
    .filter((r): r is GoogleReview => r !== null);

  if (reviews.length === 0) return null;

  return {
    rating: data.rating ?? null,
    reviewCount: data.userRatingCount ?? null,
    googleMapsUri: data.googleMapsUri ?? fallbackMapsUri,
    reviews,
  };
}

function shouldSync(meta: { syncedAt: Date; placeId: string } | null, placeId: string) {
  if (!meta) return true;
  if (meta.placeId !== placeId) return true;
  return Date.now() - meta.syncedAt.getTime() > SYNC_MAX_AGE_MS;
}

/** Avis Google Maps : synchronise depuis l’API, accumule les nouveaux en base, puis lit tout. */
export async function getGoogleReviews(): Promise<GoogleReviewsSummary | null> {
  noStore();
  const apiKey = process.env.GOOGLE_PLACES_API_KEY?.trim();
  const placeId =
    process.env.GOOGLE_PLACE_ID?.trim() ?? SITE.googlePlaceId?.trim();

  if (!placeId) return readReviewsFromDb();

  const meta = await prisma.googleReviewsMeta.findUnique({
    where: { id: META_ID },
  });
  const storedReviewCount = await prisma.googleReview.count();

  if (
    apiKey &&
    (shouldSync(meta, placeId) || storedReviewCount === 0)
  ) {
    const fetched = await fetchReviewsFromGoogle(apiKey, placeId);
    if (fetched) {
      await saveReviewsToDb(placeId, fetched);
    }
  }

  return readReviewsFromDb();
}
