// src/services/aiAnalysisService.ts
import { Flyer, FlyerItem, MasterGroceryItem, GroundedResponse, Source } from '../types';
import * as aiApiClient from './aiApiClient';
import { logger } from './logger.client';

interface RawSource {
  web?: {
    uri?: string;
    title?: string;
  };
}

/**
 * A service class to encapsulate all AI analysis API calls and related business logic.
 * This decouples the React components and hooks from the data fetching implementation.
 */
export class AiAnalysisService {
  /**
   * Fetches quick insights for a given set of flyer items.
   * @param items - The flyer items to analyze.
   * @returns The string result from the API.
   */
  async getQuickInsights(items: FlyerItem[]): Promise<string> {
    logger.info('[AiAnalysisService] getQuickInsights called.');
    return aiApiClient.getQuickInsights(items).then((res) => res.json());
  }

  /**
   * Fetches a deep dive analysis for a given set of flyer items.
   * @param items - The flyer items to analyze.
   * @returns The string result from the API.
   */
  async getDeepDiveAnalysis(items: FlyerItem[]): Promise<string> {
    logger.info('[AiAnalysisService] getDeepDiveAnalysis called.');
    return aiApiClient.getDeepDiveAnalysis(items).then((res) => res.json());
  }

  /**
   * Performs a web search based on the flyer items.
   * @param items - The flyer items to use as context for the search.
   * @returns A grounded response with text and sources.
   */
  async searchWeb(items: FlyerItem[]): Promise<GroundedResponse> {
    logger.info('[AiAnalysisService] searchWeb called.');
    // Construct a query string from the item names.
    const query = items.map((item) => item.item).join(', ');
    // The API client returns a specific shape that we need to await the JSON from
    const response: { text: string; sources: RawSource[] } = await aiApiClient
      .searchWeb(query)
      .then((res) => res.json());
    // Normalize sources to a consistent format.
    const mappedSources = (response.sources || []).map(
      (s: RawSource) =>
        (s.web ? { uri: s.web.uri || '', title: s.web.title || 'Untitled' } : { uri: '', title: 'Untitled' }) as Source,
    );
    return { ...response, sources: mappedSources };
  }

  /**
   * Plans a shopping trip using maps and the user's location.
   * @param items - The flyer items for the trip.
   * @param store - The store associated with the flyer.
   * @returns A grounded response with the trip plan and sources.
   */
  async planTripWithMaps(items: FlyerItem[], store: Flyer['store']): Promise<GroundedResponse> {
    logger.info('[AiAnalysisService] planTripWithMaps called.');
    // Encapsulate geolocation logic within the service.
    const userLocation = await this.getCurrentLocation();
    return aiApiClient.planTripWithMaps(items, store, userLocation).then((res) => res.json());
  }

  /**
   * Compares prices for a user's watched items.
   * @param watchedItems - The list of master grocery items to compare.
   * @returns A grounded response with the price comparison.
   */
  async compareWatchedItemPrices(watchedItems: MasterGroceryItem[]): Promise<GroundedResponse> {
    logger.info('[AiAnalysisService] compareWatchedItemPrices called.');
    const response: { text: string; sources: RawSource[] } = await aiApiClient
      .compareWatchedItemPrices(watchedItems)
      .then((res) => res.json());
    // Normalize sources to a consistent format.
    const mappedSources = (response.sources || []).map(
      (s: RawSource) =>
        (s.web ? { uri: s.web.uri || '', title: s.web.title || 'Untitled' } : { uri: '', title: 'Untitled' }) as Source,
    );
    return { ...response, sources: mappedSources };
  }

  /**
   * Generates an image based on a provided text prompt (e.g., a meal plan).
   * @param prompt - The text to use for image generation.
   * @returns A base64 encoded string of the generated image.
   */
  async generateImageFromText(prompt: string): Promise<string> {
    logger.info('[AiAnalysisService] generateImageFromText called.');
    return aiApiClient.generateImageFromText(prompt).then((res) => res.json());
  }

  /**
   * A helper to promisify the Geolocation API.
   * @returns A promise that resolves with the user's coordinates.
   * @private
   */
  private getCurrentLocation(): Promise<GeolocationCoordinates> {
    return new Promise((resolve, reject) => {
      if (!navigator.geolocation) {
        return reject(new Error('Geolocation is not supported by your browser.'));
      }
      navigator.geolocation.getCurrentPosition(
        (pos) => resolve(pos.coords),
        (err) => reject(err),
      );
    });
  }
}
