import { config } from '../config.js';

const googlePlaceDetailsBaseUrl = 'https://places.googleapis.com/v1/places';

export const googlePlaceDetailsFieldMask = [
  'id',
  'displayName',
  'location',
  'primaryType',
  'types',
  'businessStatus',
  'rating',
  'userRatingCount',
  'regularOpeningHours',
  'currentOpeningHours',
  'photos',
].join(',');

export type GooglePlacePhoto = {
  name?: string;
  widthPx?: number;
  heightPx?: number;
  authorAttributions?: Array<{
    displayName?: string;
    uri?: string;
    photoUri?: string;
  }>;
};

export type GooglePlaceDetails = {
  id?: string;
  displayName?: {
    text?: string;
  };
  primaryType?: string;
  types?: string[];
  businessStatus?: string;
  rating?: number;
  userRatingCount?: number;
  regularOpeningHours?: unknown;
  currentOpeningHours?: unknown;
  photos?: GooglePlacePhoto[];
  location?: {
    latitude?: number;
    longitude?: number;
  };
};

export type GooglePlaceEnrichment = {
  name: string;
  latitude: number;
  longitude: number;
  googlePrimaryType: string | null;
  googleTypes: string[];
  googleBusinessStatus: string | null;
  googleRating: number | null;
  googleUserRatingCount: number | null;
  googleRegularOpeningHours: unknown | null;
  googleCurrentOpeningHours: unknown | null;
  googlePhotos: GooglePlacePhoto[];
  googlePayload: GooglePlaceDetails;
  googlePayloadFetchedAt: Date;
};

function assertGooglePlacesKey() {
  if (config.googlePlacesApiKey === '') {
    throw new Error('GOOGLE_PLACES_API_KEY is required for Google Places.');
  }
}

export async function fetchGooglePlaceDetails(
  googlePlaceId: string,
): Promise<GooglePlaceDetails> {
  assertGooglePlacesKey();

  const response = await fetch(
    `${googlePlaceDetailsBaseUrl}/${encodeURIComponent(googlePlaceId)}`,
    {
      headers: {
        'X-Goog-Api-Key': config.googlePlacesApiKey,
        'X-Goog-FieldMask': googlePlaceDetailsFieldMask,
      },
    },
  );

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`Google Place Details failed: ${response.status} ${body}`);
  }

  return (await response.json()) as GooglePlaceDetails;
}

export function normalizeGooglePlaceDetails(
  fallback: {
    name: string;
    latitude: number;
    longitude: number;
  },
  payload: GooglePlaceDetails,
): GooglePlaceEnrichment {
  return {
    name: payload.displayName?.text?.trim() || fallback.name,
    latitude: payload.location?.latitude ?? fallback.latitude,
    longitude: payload.location?.longitude ?? fallback.longitude,
    googlePrimaryType: payload.primaryType ?? null,
    googleTypes: payload.types ?? [],
    googleBusinessStatus: payload.businessStatus ?? null,
    googleRating: payload.rating ?? null,
    googleUserRatingCount: payload.userRatingCount ?? null,
    googleRegularOpeningHours: payload.regularOpeningHours ?? null,
    googleCurrentOpeningHours: payload.currentOpeningHours ?? null,
    googlePhotos: (payload.photos ?? [])
      .filter((photo) => Boolean(photo.name))
      .slice(0, 3),
    googlePayload: payload,
    googlePayloadFetchedAt: new Date(),
  };
}

export async function fetchGooglePlacePhotoMedia(input: {
  photoName: string;
  maxWidthPx: number;
}) {
  assertGooglePlacesKey();
  if (
    !input.photoName.startsWith('places/') ||
    !input.photoName.includes('/photos/')
  ) {
    throw new Error('Google photo name is invalid.');
  }
  if (
    !Number.isInteger(input.maxWidthPx) ||
    input.maxWidthPx < 1 ||
    input.maxWidthPx > 4800
  ) {
    throw new Error('Google photo maxWidthPx must be from 1 to 4800.');
  }

  const response = await fetch(
    `https://places.googleapis.com/v1/${input.photoName}/media?maxWidthPx=${input.maxWidthPx}`,
    { headers: { 'X-Goog-Api-Key': config.googlePlacesApiKey } },
  );

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`Google Place Photo failed: ${response.status} ${body}`);
  }

  return {
    contentType: response.headers.get('content-type') ?? 'image/jpeg',
    bytes: Buffer.from(await response.arrayBuffer()),
  };
}
