import React, { createContext, useCallback, useContext, useEffect, useMemo } from 'react';
import { useAtomValue } from 'jotai';
import {
  Configuration,
  ResponseCategory,
  ResponseError,
  SearchApi,
} from '@spiffy-ai/commerce-api-client';
import { baseUrlAtom } from 'src/atoms/envive/enviveConfig';
import Logger from 'src/application/logging/logger';
import { useAppDetails } from 'src/hooks/AppDetails/useAppDetails';
import { useEnviveConfig } from 'src/contexts/enviveConfigContext';
import { SessionRestartRequired } from 'src/types/exceptions/sessionExceptions';
import { UnsupportedProductException } from 'src/types/exceptions/unsupportedProductExceptions';
import { SearchParams, SearchResult } from 'src/application/models/api/search';
import {
  clearSearchServiceFunction,
  setSearchServiceFunction,
} from 'src/atoms/search/searchServiceAdapter';

const logger = new Logger('searchContext');

const transformProductResponses = (products: SearchResult['products']) =>
  products.map(data => ({
    id: data.id,
    responseId: data.response_id,
    category: ResponseCategory.Product,
    description: data.description,
    imageUrl: data.image_url,
    imageUrls: data.image_urls,
    title: data.title,
    url: data.url,
    originalPrice: data.original_price,
    salePrice: data.sale_price,
    averageRating: data.average_rating,
    numberReviews: data.number_reviews,
    metadata: data.metadata,
    isForGrid: data.is_for_grid,
    colors: data.colors,
    sizes: data.sizes,
    filters: data.filters,
  }));

async function errorResponseBody(error: ResponseError) {
  try {
    return await error.response.json();
  } catch {
    return {};
  }
}

async function throwSessionRestartRequiredIf(errorMsg: string, error: unknown) {
  if (!(error instanceof ResponseError)) {
    logger.logInfo(errorMsg, error);
    throw error;
  }

  const errorResponse = await errorResponseBody(error);
  if (
    errorResponse?.message?.toLowerCase() === 'unsupported product' ||
    errorResponse?.app_code?.toUpperCase() === 'PRODUCT_NOT_FOUND'
  ) {
    throw new UnsupportedProductException();
  } else if (
    errorResponse?.app_code?.toUpperCase() === 'RESTART_SESSION' ||
    errorResponse?.sub_code?.toUpperCase() === 'NOT_FOUND'
  ) {
    logger.logInfo(
      'Session does not exist. Re-start session',
      error,
      error.response,
      errorResponse,
    );
    throw new SessionRestartRequired();
  }

  logger.logInfo(errorMsg, error);
  throw error;
}

interface SearchContextValue {
  searchProducts: (params: SearchParams) => Promise<SearchResult>;
  isReady: boolean;
}

const SearchContext = createContext<SearchContextValue | undefined>(undefined);

export const SearchProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const { orgLevelApiKey, publicKey } = useEnviveConfig();
  const apiKey = orgLevelApiKey || publicKey; // Replicate apiKeyAtom logic
  const appDetails = useAppDetails();
  const baseUrl = useAtomValue(baseUrlAtom);

  const isReady = Boolean(apiKey && appDetails && baseUrl);

  const searchApi = useMemo(() => {
    if (!isReady) return null;

    const config: Configuration = new Configuration({
      accessToken: apiKey,
      basePath: baseUrl,
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      },
    });
    return new SearchApi(config);
  }, [apiKey, baseUrl, isReady]);

  const searchProducts = useCallback(
    async (params: SearchParams): Promise<SearchResult> => {
      if (!isReady || !searchApi) {
        throw new Error('SearchService not ready - missing dependencies');
      }

      try {
        const response = await searchApi.v1SearchQueryGet({
          query: params.query,
          limit: params.limit,
          org_id: appDetails.orgId,
          user_id: appDetails.userId,
        });
        const { products, filters, search_response_id: searchResponseId } = response;

        return {
          products: transformProductResponses(products) || [],
          filters: filters || [],
          totalProductCount: products?.length || 0,
          searchResponseId: searchResponseId || '',
        };
      } catch (error) {
        await throwSessionRestartRequiredIf('Failed to search products', error);
        // This part will not be reached if an exception is thrown, but it's required for type safety
        return {
          products: [],
          filters: [],
          totalProductCount: 0,
          searchResponseId: '',
        };
      }
    },
    [searchApi, isReady, appDetails],
  );

  // Register the search function with the adapter for use in atoms
  useEffect(() => {
    if (isReady) {
      setSearchServiceFunction(searchProducts);
    } else {
      clearSearchServiceFunction();
    }

    return () => {
      clearSearchServiceFunction();
    };
  }, [searchProducts, isReady]);

  const value = useMemo(
    () => ({
      searchProducts,
      isReady,
    }),
    [searchProducts, isReady],
  );

  return <SearchContext.Provider value={value}>{children}</SearchContext.Provider>;
};

export const useSearchService = () => {
  const context = useContext(SearchContext);
  if (!context) {
    throw new Error('useSearchService must be used within a SearchProvider');
  }
  return context;
};
