/**
 * Google Places API (New) — Direct REST client.
 *
 * Replaces Apify `compass~crawler-google-places` actor with official API.
 * Benefits: 1-2s latency (vs 30-120s), stable contract, ToS compliant.
 *
 * API docs: https://developers.google.com/maps/documentation/places/web-service/op-overview
 *
 * Pricing (per 1K calls):
 *   - Text Search (Basic):   $32   → but we use field mask to reduce
 *   - Place Details (Basic):  $0   (id-only fields)
 *   - Place Details (Contact): $3  (phone, website)
 *   - Place Details (Atmos):   $5  (reviews, rating)
 *
 * Typical cost per clinic: ~$0.04 (1 Text Search + 1 Details)
 */

import { fetchWithRetry } from "./retry.ts";

// ─── Types ───

export interface GooglePlaceResult {
  name: string;           // e.g. "뷰성형외과의원"
  rating: number | null;  // e.g. 4.3
  reviewCount: number;    // e.g. 387
  address: string;        // formatted address
  phone: string;          // international format
  clinicWebsite: string;  // clinic's own website
  mapsUrl: string;        // Google Maps URL
  category: string;       // primary type
  placeId: string;        // for future lookups
  openingHours: Record<string, unknown> | null;
  topReviews: {
    stars: number;
    text: string;
    publishedAtDate: string;
  }[];
}

// ─── Constants ───

const PLACES_BASE = "https://places.googleapis.com/v1";

// Fields we need from Text Search (covers Basic + Contact + Atmosphere tiers)
const TEXT_SEARCH_FIELDS = [
  "places.id",
  "places.displayName",
  "places.formattedAddress",
  "places.rating",
  "places.userRatingCount",
  "places.nationalPhoneNumber",
  "places.internationalPhoneNumber",
  "places.websiteUri",
  "places.googleMapsUri",
  "places.primaryType",
  "places.primaryTypeDisplayName",
  "places.regularOpeningHours",
  "places.reviews",
].join(",");

// ─── Main Function ───

/**
 * Search for a clinic on Google Places and return structured data.
 * Tries multiple queries in order until a match is found.
 */
export async function searchGooglePlace(
  clinicName: string,
  address: string | undefined,
  apiKey: string,
): Promise<GooglePlaceResult | null> {
  const queries = [
    `${clinicName} 성형외과`,
    clinicName,
    `${clinicName} ${address || "강남"}`,
  ];

  for (const query of queries) {
    const result = await textSearch(query, apiKey);
    if (result) return result;
  }

  return null;
}

/**
 * Google Places Text Search (New) API call.
 * Returns the first matching place with full details in a single call.
 */
async function textSearch(
  query: string,
  apiKey: string,
): Promise<GooglePlaceResult | null> {
  const res = await fetchWithRetry(
    `${PLACES_BASE}/places:searchText`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Goog-Api-Key": apiKey,
        "X-Goog-FieldMask": TEXT_SEARCH_FIELDS,
      },
      body: JSON.stringify({
        textQuery: query,
        languageCode: "ko",
        regionCode: "KR",
        maxResultCount: 3,
      }),
    },
    {
      maxRetries: 1,
      timeoutMs: 15000,
      label: `google-places:textSearch`,
    },
  );

  if (!res.ok) {
    const errText = await res.text().catch(() => "");
    throw new Error(`Google Places Text Search ${res.status}: ${errText}`);
  }

  const data = await res.json();
  const places = data.places as Record<string, unknown>[] | undefined;

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

  // Use first result (most relevant)
  return mapPlaceToResult(places[0]);
}

/**
 * Map Google Places API response to our internal structure.
 * Maintains the same field names as the old Apify-based structure
 * for backward compatibility with transformReport.ts and UI components.
 */
function mapPlaceToResult(place: Record<string, unknown>): GooglePlaceResult {
  const displayName = place.displayName as Record<string, unknown> | undefined;
  const openingHours = place.regularOpeningHours as Record<string, unknown> | null;
  const reviews = (place.reviews as Record<string, unknown>[]) || [];
  const primaryTypeDisplay = place.primaryTypeDisplayName as Record<string, unknown> | undefined;

  return {
    name: (displayName?.text as string) || "",
    rating: (place.rating as number) || null,
    reviewCount: (place.userRatingCount as number) || 0,
    address: (place.formattedAddress as string) || "",
    phone: (place.internationalPhoneNumber as string) || (place.nationalPhoneNumber as string) || "",
    clinicWebsite: (place.websiteUri as string) || "",
    mapsUrl: (place.googleMapsUri as string) || "",
    category: (primaryTypeDisplay?.text as string) || (place.primaryType as string) || "",
    placeId: (place.id as string) || "",
    openingHours: openingHours,
    topReviews: reviews.slice(0, 10).map((r) => {
      const authorAttribution = r.authorAttribution as Record<string, unknown> | undefined;
      const publishTime = r.publishTime as string || "";
      return {
        stars: (r.rating as number) || 0,
        text: ((r.text as Record<string, unknown>)?.text as string || "").slice(0, 500),
        publishedAtDate: publishTime,
      };
    }),
  };
}

/**
 * Get Place Details by Place ID — for future use when we have cached placeIds
 * in the clinic_registry table. Cheaper than Text Search.
 */
export async function getPlaceDetails(
  placeId: string,
  apiKey: string,
): Promise<GooglePlaceResult | null> {
  const fieldMask = [
    "id",
    "displayName",
    "formattedAddress",
    "rating",
    "userRatingCount",
    "nationalPhoneNumber",
    "internationalPhoneNumber",
    "websiteUri",
    "googleMapsUri",
    "primaryType",
    "primaryTypeDisplayName",
    "regularOpeningHours",
    "reviews",
  ].join(",");

  const res = await fetchWithRetry(
    `${PLACES_BASE}/places/${placeId}`,
    {
      method: "GET",
      headers: {
        "X-Goog-Api-Key": apiKey,
        "X-Goog-FieldMask": fieldMask,
      },
    },
    {
      maxRetries: 1,
      timeoutMs: 10000,
      label: `google-places:details`,
    },
  );

  if (!res.ok) {
    const errText = await res.text().catch(() => "");
    throw new Error(`Google Places Details ${res.status}: ${errText}`);
  }

  const place = await res.json();
  return mapPlaceToResult(place as Record<string, unknown>);
}
