'use client';

import { SearchResult } from '@datocms/cma-client-browser/dist/types/generated/resources';

import { SEARCH_PAGE_SIZE } from '@/app/(root)/search/utils';
import { components } from '@/lib/gen';

import { RootStore, Substore } from './rootStore';
import { makeAutoObservableSubstore } from './utils';

export type SearchTypeEnum = components['schemas']['SearchTypeEnum'];
export type SearchResult = components['schemas']['BaseSearchResult'];
export type SearchRankingEnum = components['schemas']['SearchRankingEnum'];

export class QueryParams {
  searchType: SearchTypeEnum = 'public_song';
  query: string = '';
  isPublic: boolean = true;
  rankBy: SearchRankingEnum = 'trending';
  fromIndex: number = 0;
  pageSize: number = 20;
  is_instrumental?: boolean;
  model_version?: string;

  constructor({
    searchType = 'public_song' as SearchTypeEnum,
    query = '',
    isPublic = true,
    rankBy = 'trending' as SearchRankingEnum,
    fromIndex = 0,
    pageSize = 20,
    is_instrumental,
    model_version,
  }: {
    searchType?: SearchTypeEnum;
    query?: string;
    isPublic?: boolean;
    rankBy?: SearchRankingEnum;
    fromIndex?: number;
    pageSize?: number;
    is_instrumental?: boolean;
    model_version?: string;
  } = {}) {
    this.searchType = searchType;
    this.query = query;
    this.isPublic = isPublic;
    this.rankBy = rankBy;
    this.fromIndex = fromIndex;
    this.pageSize = pageSize;
    this.is_instrumental = is_instrumental;
    this.model_version = model_version;
  }

  getSearchCacheKey = () => {
    return [
      this.searchType,
      this.query,
      this.rankBy,
      this.isPublic,
      this.is_instrumental === undefined ? '' : this.is_instrumental,
      this.model_version === undefined ? '' : this.model_version,
    ].join('|');
  };
}

const PUBLIC_SONG = 'public_song';

export class SearchStore implements Substore {
  searchTerm: string = '';
  searchType: SearchTypeEnum = PUBLIC_SONG;
  searchRankingType: SearchRankingEnum = 'most_relevant';

  librarySearchTerm: string = '';

  searchResultCache: Record<string, SearchResult> = {};

  setSearchType = (searchType: SearchTypeEnum) => {
    this.searchType = searchType;
  };

  setSearchRankingType = (rankingValue: string) => {
    this.searchRankingType = rankingValue as SearchRankingEnum;
  };

  readonly root: RootStore;
  get apiClient() {
    return this.root.apiClient;
  }
  // DO NOT ADD INIT LOGIC IN constructor. this will run on every page! considering adding any it in the component init logic instead
  constructor(root: RootStore) {
    this.root = root;
    makeAutoObservableSubstore(this);
  }

  simpleSearch = async (
    term: string,
    searchType: SearchTypeEnum = PUBLIC_SONG
  ) => {
    const queryParams = new QueryParams({
      searchType,
      query: term,
      isPublic: true,
      rankBy: 'most_relevant',
      fromIndex: 0,
      pageSize: 50,
    });
    const { data } = await this.apiClient.POST('/api/search/', {
      body: {
        search_queries: [
          {
            name: queryParams.searchType + queryParams.query,
            search_type: queryParams.searchType,
            term: queryParams.query,
            from_index: queryParams.fromIndex,
            size: queryParams.pageSize,
            rank_by: queryParams.rankBy,
          },
        ],
      },
    });
    if (!data) return;
    const clips = data.result[searchType + term].result;
    return clips;
  };

  searchData = async (
    term: string,
    isIncremental: boolean,
    searchType: SearchTypeEnum = PUBLIC_SONG,
    rankBy: SearchRankingEnum = 'trending',
    is_instrumental?: boolean,
    model_version?: string,
    pageSize: number = SEARCH_PAGE_SIZE
  ) => {
    if (
      searchType === 'public_persona' &&
      !this.root.session.flags?.['persona-search']
    ) {
      return;
    }

    const queryParams = new QueryParams({
      searchType,
      query: term,
      isPublic: true,
      rankBy,
      fromIndex: 0,
      pageSize: pageSize,
      is_instrumental,
      model_version,
    });

    const cacheKey = queryParams.getSearchCacheKey();
    const cachedResult = this.searchResultCache[cacheKey];

    if (isIncremental === false && this.searchResultCache[cacheKey]) {
      return;
    }

    if (isIncremental) {
      queryParams.fromIndex =
        (cachedResult?.from_index ?? 0) + (cachedResult?.page_size ?? 0);
    }

    const { data } = await this.apiClient.POST('/api/search/', {
      body: {
        search_queries: [
          {
            name: queryParams.searchType + queryParams.query,
            search_type: queryParams.searchType,
            term: queryParams.query,
            from_index: queryParams.fromIndex,
            size: queryParams.pageSize,
            rank_by: queryParams.rankBy,
            is_instrumental: queryParams.is_instrumental,
            model_version: queryParams.model_version,
          },
        ],
      },
    });
    if (!data) return;
    const clips = data.result[searchType + term].result;
    if (
      isIncremental &&
      cachedResult &&
      (cachedResult.from_index ?? 0) + (cachedResult.page_size ?? 0) <=
        queryParams.fromIndex
    ) {
      data.result[searchType + term].result =
        this.searchResultCache[queryParams.getSearchCacheKey()].result.concat(
          clips
        );
    }
    this.searchResultCache[queryParams.getSearchCacheKey()] =
      data.result[searchType + term];
  };

  librarySearch = async (term: string, page: number = 0) => {
    const PAGE_SIZE = 20;
    const queryParams = {
      query: term,
      from_index: page * PAGE_SIZE,
    };

    const { data } = await this.apiClient.POST('/api/search/', {
      body: {
        search_queries: [
          {
            search_type: 'library_song',
            name: 'library_song',
            term: queryParams.query,
            from_index: queryParams.from_index,
            rank_by: 'most_relevant',
          },
        ],
      },
    });

    if (!data) return;

    return data.result['library_song'];
  };

  libraryPersonaSearch = async (term: string, page: number = 0) => {
    const PAGE_SIZE = 20;
    const queryParams = {
      query: term,
      from_index: page * PAGE_SIZE,
    };

    const { data } = await this.apiClient.POST('/api/search/', {
      body: {
        search_queries: [
          {
            search_type: 'library_persona',
            name: 'library_persona',
            term: queryParams.query,
            from_index: queryParams.from_index,
            rank_by: 'most_relevant',
            is_public: false,
          },
        ],
      },
    });

    if (!data) return;

    return data.result['library_persona'];
  };

  libraryPlaylistSearch = async (term: string, page: number = 0) => {
    const PAGE_SIZE = 20;
    const queryParams = {
      query: term,
      from_index: page * PAGE_SIZE,
    };

    const { data } = await this.apiClient.POST('/api/search/', {
      body: {
        search_queries: [
          {
            search_type: 'library_playlist',
            name: 'library_playlist',
            term: queryParams.query,
            from_index: queryParams.from_index,
            rank_by: 'most_relevant',
          },
        ],
      },
    });

    if (!data) return;

    return data.result['library_playlist'];
  };
}
