import { UserEventCategory } from '@spiffy-ai/commerce-api-client';
import { v4 as uuid } from 'uuid';
import { atom } from 'jotai';
import {
  Message,
  MessageRole,
  MessageType,
  ProductResponseAttributes,
} from 'src/application/models';
import {
  messagesAtom,
  replyEventCategoryAtom,
  userHasRepliedAtom,
  userQueryAtom,
} from 'src/atoms/chat';
import { queueUserEventAtom } from '../chat/messageQueue';
import { newOrgConfigAtom } from '../org';
import { ProductFilters } from './productFilters';
import { ProductSorter } from './productSorter';
import { ChatSearchStateType, ProductSorting } from './types';

// NOTE:
// This is a legacy version of search that needs to be decoupled and removed.
// the new search.ts file in this directory is likely what you're looking for.

export const chatSearchStateAtom = atom<ChatSearchStateType>('entrypoint');
export const chatSearchHasProductsAtom = atom<boolean>(false);
export const chatSearchProductSortingAtom = atom<ProductSorting>(ProductSorting.FEATURED);

const filterProductList = (message: Message) =>
  message.type === MessageType.Product && message.metadata.isForGrid === true;
const filterFilterList = (message: Message) => message.type === MessageType.ProductSearchFilter;

export type ChatSearchTurn = {
  id: string;
  generatedQuery?: string;
  productCount: number;
  productList: ProductResponseAttributes['attributes'][];
  filterList: string[];
  messages: Message[];
};

export const chatSearchIsLoadingAtom = atom(false);

const buildChatSearchTurn = (messages: Message[]): [string, ChatSearchTurn] | null => {
  const queryMessage = messages.find(message => message.type === MessageType.ProductSearch);
  if (!queryMessage) {
    return null;
  }
  const { generatedQuery, productCount } = queryMessage.metadata ?? {};
  const productList = messages.filter(filterProductList).map(message => (message as any).metadata);
  const filterList = messages.filter(filterFilterList).map(message => message.metadata.filterName);
  return [
    queryMessage.id,
    {
      id: queryMessage.id,
      generatedQuery,
      productCount: productCount || productList.length,
      productList,
      filterList,
      messages,
    },
  ];
};

const chatSearchFilterConfigAtom = atom(get => {
  const newOrgConfig = get(newOrgConfigAtom);
  return newOrgConfig?.frontendConfig?.uiConfigs?.searchConfig?.searchFilterConfig || [];
});

const searchMapAtom = atom(get => {
  const messages = get(messagesAtom);
  return Object.fromEntries(messages.map(buildChatSearchTurn).filter(v => v !== null));
});

const internalChatSearchParamsAtom = atom<{
  id: string | null;
  query: string | null;
}>({
  id: null,
  query: null,
});

// Selected filter options with compound objects for better type safety and display
export type SelectedChatSearchFilterOption = {
  id: string; // "filterId:filterItemId" - maintains backward compatibility for comparisons
  displayName: string;
  filterId: string; // Filter category
  filterItemId: string; // Filter value
};

export const selectedFilterOptionsAtom = atom<SelectedChatSearchFilterOption[]>([]);

export const additiveDynamicFiltersAtom = atom(get => {
  const newOrgConfig = get(newOrgConfigAtom);
  return newOrgConfig?.frontendConfig?.uiConfigs?.searchConfig?.additiveDynamicFilters ?? false;
});

export const createChatSearchFilterOption = (
  filterId: string,
  filterItemId: string,
  displayName: string,
): SelectedChatSearchFilterOption => ({
  id: `${filterId}:${filterItemId}`,
  displayName,
  filterId,
  filterItemId,
});

const handleSearchAtom = atom(null, (get, set, message: Message) => {
  if (message.type !== MessageType.Search) {
    return;
  }

  const querySearch = message.metadata.searchTerm;

  set(replyEventCategoryAtom, UserEventCategory.Search);
  set(userQueryAtom, querySearch);
  set(messagesAtom, [...get(messagesAtom), [message]]);
  set(userHasRepliedAtom, true);

  set(queueUserEventAtom, {
    eventId: message.id,
    category: UserEventCategory.Search,
    createdAt: message.createdAt,
    attributes: {
      searchTerm: message.metadata.searchTerm,
      selectedFilters: message.metadata.selectedFilters,
    },
  });
});

// This internal atom is used to store the search params
export const chatSearchParamsAtom = atom(
  get => get(internalChatSearchParamsAtom),
  (_, set, value: { id: string | null; query: string | null }) => {
    set(internalChatSearchParamsAtom, value);
  },
);

export const chatSearchQueryAtom = atom(
  get => get(chatSearchParamsAtom)?.query ?? null,
  (get, set, value: string | null) => {
    const searchParams = get(chatSearchParamsAtom);
    set(chatSearchParamsAtom, { id: searchParams.id, query: value });
  },
);

export const chatSearchIdAtom = atom(
  get => get(chatSearchParamsAtom)?.id ?? null,
  (get, set, value: string | null) => {
    const searchParams = get(chatSearchParamsAtom);
    set(chatSearchParamsAtom, { id: value, query: searchParams.query });
  },
);

export const ChatSearchTurnAtom = atom(get => {
  const searchFilterConfig = get(chatSearchFilterConfigAtom);
  const searchMap = get(searchMapAtom);
  const chatSearchId = get(chatSearchIdAtom);
  if (!chatSearchId) {
    return null;
  }
  const ChatSearchTurn = searchMap[chatSearchId];
  if (!ChatSearchTurn) {
    return null;
  }
  const productSorting = get(chatSearchProductSortingAtom);
  const selectedFilterOptions = get(selectedFilterOptionsAtom);
  const additiveDynamicFilters = get(additiveDynamicFiltersAtom);
  const filteredProducts = ProductFilters.filterProducts(
    ChatSearchTurn.productList,
    searchFilterConfig,
    selectedFilterOptions,
    additiveDynamicFilters,
  );

  const filteredAndSortedProducts = ProductSorter.sort(filteredProducts, productSorting);

  return {
    ...searchMap[chatSearchId],
    productList: filteredAndSortedProducts,
    productCount: filteredAndSortedProducts.length,
  };
});

// TODO: Work on deprecating this atom in favor of using ChatSearchTurnAtom directly
export const chatSearchProducts = atom(get => {
  const ChatSearchTurn = get(ChatSearchTurnAtom);
  return ChatSearchTurn?.productList ?? [];
});

export const chatSearchFiltersAtom = atom(get => {
  const searchFilterConfig = get(chatSearchFilterConfigAtom);
  const searchMap = get(searchMapAtom);
  const chatSearchId = get(chatSearchIdAtom);

  if (chatSearchId && searchMap[chatSearchId]) {
    const products = searchMap[chatSearchId].productList;
    const selectedFilterOptions = get(selectedFilterOptionsAtom);
    return ProductFilters.getFiltersForProducts(
      products,
      searchFilterConfig,
      selectedFilterOptions,
    );
  }
  return [];
});

// For search we use the following query params:
// es: true - this indicates that we are in a search state
// esq: query - this is the current search query
// esi: search id - this is the unique id for the search results that we are displaying. It is pulled from the message id from the turn that includes the search results

export const setSearchParams = (query: string) => {
  const url = new URL(window.location.href);
  url.searchParams.set('es', 'true');
  url.searchParams.set('esq', query);
  window.history.pushState({}, '', url);
};

export const getSearchParamsAtom = atom(() => {
  const params = new URLSearchParams(window.location.search);
  return {
    es: params.get('es'),
    esq: params.get('esq'),
  };
});

export const initiateChatSearchAtom = atom(null, (_, set, query: string) => {
  set(chatSearchIsLoadingAtom, true);
  // Update URL and manually trigger state change (since pushState doesn't trigger popstate)
  const url = new URL(window.location.href);
  url.searchParams.set('es', 'true');
  url.searchParams.set('esq', query);
  window.history.pushState({}, '', url);

  // Manually trigger page variant re-evaluation by dispatching a popstate event
  // This ensures the page variant system detects the URL change and mounts search results
  window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));

  set(chatSearchParamsAtom, { id: null, query });
  set(handleSearchAtom, {
    id: uuid(),
    role: MessageRole.User,
    type: MessageType.Search,
    createdAt: new Date().toISOString(),
    metadata: {
      searchTerm: query,
      // Enabling this will enable the new backend search
      selectedFilters: [],
    },
  });
});

export const handleSearchResultsAtom = atom(null, (get, set, message: Message | undefined) => {
  if (!message || message.type !== MessageType.ProductSearch) {
    return;
  }

  const { productCount, generatedQuery } = message.metadata;
  if (productCount !== 0) {
    // Clear the filter options when we get new products
    set(selectedFilterOptionsAtom, []);
    // TODO: Should we still do any of these things?
    set(chatSearchStateAtom, 'product-page');
    const currentSearchParams = get(chatSearchParamsAtom);
    const url = new URL(window.location.href);
    url.searchParams.set('es', 'true');
    url.searchParams.set('esq', generatedQuery);
    url.searchParams.set('esi', message.id);
    window.history.pushState({}, '', url);
    if (currentSearchParams.id === null) {
      // If the id is null, we need to replace the state
      window.history.replaceState(null, '', url);
    } else {
      // If the query is different, we need to push a new state
      window.history.pushState(null, '', url);
    }
    set(chatSearchParamsAtom, { id: message.id, query: generatedQuery });
  }
});

export const setChatSearchParamsAtom = atom(null, (_, set, query: string) => {
  const url = new URL(window.location.href);
  url.searchParams.set('es', 'true');
  url.searchParams.set('esq', query);
  window.history.pushState({}, '', url);
  set(chatSearchParamsAtom, { id: null, query });
});
