import { Clip, Playlist } from '@/state/clipStore';

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

const defaultFilters = {
  clips: {
    liked: false,
    hide_disliked: true,
    hide_gen_stems: true,
    hide_studio_clips: true,

    public: false,
    full_song: false,
    is_suno_short: false,
    is_cover: false,
    is_upsample: false,
    is_extend: false,
    is_persona: false,
    is_uploaded_audio: false,
    is_infill: false,
    is_gen_stem: false,
    page: 0,
    query: '',
  },
  playlists: {
    page: 0,
    trashed: false,
    query: '',
  },
};

const pageSize = 50;
export class LibraryStore implements Substore {
  activeClip?: Clip = undefined;
  activePlaylist?: Playlist = undefined;

  clipIds: string[] = [];
  numTotalClips = 0; // Deprecated: kept for backward compatibility, use hasMoreClips instead
  hasMoreClips = false;

  playlistIds: string[] = [];
  numTotalPlaylists = 0;

  followedProfiles: any[] = [];
  followers: any[] = [];

  loadingPlaylists: boolean = false;
  loadingLikedPlaylists: boolean = false;

  isLoaded = {
    clips: false,
    playlists: false,
  };

  filters = { ...defaultFilters };

  trashedClipIndices = new Map();

  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);
  }

  setInitialClips = (clips: Clip[]) => {
    this.clipIds = clips.map((clip) => clip.id);
    this.root.clips.updateClips(clips);
    this.isLoaded = { ...this.isLoaded, clips: true };

    this.updateFilters({});
  };

  setActiveClip(clip: any) {
    this.activeClip = clip;
  }

  setActivePlaylist(playlist: Playlist | undefined) {
    this.activePlaylist = playlist;
  }

  updateFilters = (
    filters: any,
    filterType: 'clips' | 'playlists' = 'clips'
  ) => {
    if (typeof filters.page === 'undefined') {
      this.filters[filterType].page = 0;
    }
    this.filters[filterType] = {
      ...this.filters[filterType],
      ...filters,
    };
    if (filterType === 'clips') {
      this.loadClips();
    }
    if (filterType === 'playlists') {
      this.loadPlaylists();
    }
  };

  updateFiltersSync = async (
    filters: any,
    filterType: 'clips' | 'playlists' = 'clips'
  ) => {
    if (typeof filters.page === 'undefined') {
      this.filters[filterType].page = 0;
    }
    this.filters[filterType] = {
      ...this.filters[filterType],
      ...filters,
    };
    if (filterType === 'clips') {
      await this.loadClips();
    }
    if (filterType === 'playlists') {
      await this.loadPlaylists();
    }
  };

  clearFilters = () => {
    this.filters = { ...defaultFilters };
  };

  incrementPage = (
    offset: number,
    filterType: 'clips' | 'playlists' = 'clips'
  ) => {
    this.updateFilters(
      { page: Math.max(0, this.filters[filterType].page + offset) },
      filterType
    );
  };

  setPageNumber = (
    newPageNumber: number,
    filterType: 'clips' | 'playlists' = 'clips'
  ) => {
    this.updateFilters({ page: newPageNumber }, filterType);
  };

  loadClips = async () => {
    // if (this.filters.clips.query) {
    //   const clips = await this.root.search.librarySearch(
    //     this.filters.clips.query,
    //     this.filters.clips.page
    //   );
    //   const resultClips = (clips as any)?.result;
    //   if (resultClips !== undefined) {
    //     this.root.clips.updateClips(resultClips);
    //     this.clipIds = resultClips.map((clip: Clip) => clip.id);
    //   }
    //   return;
    // }
    const filtersKey = JSON.stringify(this.filters);
    const { data } = await this.apiClient.GET('/api/feed/v2', {
      params: {
        query: {
          ...(this.filters.clips.liked ? { is_liked: true } : {}),
          ...(this.filters.clips.hide_disliked ? { hide_disliked: true } : {}),
          ...(this.filters.clips.hide_gen_stems
            ? { hide_gen_stems: true }
            : {}),
          ...(this.filters.clips.hide_studio_clips
            ? { hide_studio_clips: true }
            : {}),
          ...(this.filters.clips.public ? { is_public: true } : {}),
          ...(this.filters.clips.is_suno_short ? { is_suno_short: true } : {}),
          ...(this.filters.clips.full_song ? { is_full_song: true } : {}),
          ...(this.filters.clips.is_extend ? { is_extend: true } : {}),
          ...(this.filters.clips.is_cover ? { is_cover: true } : {}),
          ...(this.filters.clips.is_infill ? { is_infill: true } : {}),
          ...(this.filters.clips.is_gen_stem ? { is_gen_stem: true } : {}),
          ...(this.filters.clips.is_upsample ? { is_upsample: true } : {}),
          ...(this.filters.clips.is_persona ? { is_persona: true } : {}),
          ...(this.filters.clips.is_uploaded_audio
            ? { is_uploaded_audio: true }
            : {}),
          page: this.filters.clips.page,
          query:
            this.filters.clips.query !== ''
              ? this.filters.clips.query
              : undefined,
        },
      },
    });

    if (!data) return;

    const clipsData = data.clips;

    this.root.clips.updateClips(data.clips);
    this.numTotalClips = data.num_total_results; // Deprecated field
    this.hasMoreClips = data.has_more ?? false;
    this.isLoaded = { ...this.isLoaded, clips: true };

    if (filtersKey !== JSON.stringify(this.filters)) return;
    this.clipIds = clipsData.map((clip) => clip.id);
    this.root.clips.clipIds = clipsData.reverse().map((clip) => clip.id);
  };

  get currentClips() {
    if (!this.isLoaded) return null;
    return this.clipIds.map((id) => this.root.clips.clipById[id]);
  }

  removeFromLibrary = (clipId: string) => {
    const index = this.clipIds.indexOf(clipId);
    if (index !== -1) {
      this.trashedClipIndices.set(clipId, index);
      this.clipIds.splice(index, 1);
    }
  };

  get currentActiveClip() {
    return this.activeClip;
  }

  loadPlaylists = async (page?: number, showSharelist: boolean = false) => {
    this.loadingPlaylists = true;
    const pageToLoad = Math.max(page || 1, 1);
    const { data } = await this.apiClient.GET('/api/playlist/me', {
      params: {
        query: {
          page: pageToLoad,
          show_trashed: this.filters.playlists.trashed,
          query:
            this.filters.playlists.query !== ''
              ? this.filters.playlists.query
              : undefined,
          show_sharelist: showSharelist,
        },
      },
    });
    this.loadingPlaylists = false;

    if (!data) return;

    // TODO: Add types here.
    const cleanedData = data.playlists.map((playlist: any) => ({
      ...playlist,
      clipIds: playlist.playlist_clips
        .map((playlistClip: any) => playlistClip?.clip?.id)
        .filter((id: string | undefined) => id !== undefined),
    }));

    if (this.root.clips) {
      this.root.clips.updatePlaylists(cleanedData, pageToLoad);
      this.isLoaded = { ...this.isLoaded, playlists: true };

      this.playlistIds = data.playlists.map((playlist) => playlist.id);
      this.numTotalPlaylists = data.num_total_results;
    } else {
      console.error('Clips instance is undefined');
    }
  };

  loadFollowedProfiles = async (page?: number) => {
    const response = await this.apiClient.GET('/api/profiles/following', {
      params: {
        query: { page: page || 1 },
      },
    });
    this.followedProfiles = [
      ...this.followedProfiles.slice(0, ((page || 1) - 1) * 20),
      ...((response.data as any)?.profiles || []),
    ];
    return {
      numLoadedProfiles: ((response.data as any)?.profiles || []).length,
      numTotalProfiles: (response.data as any)?.num_total_profiles,
    };
  };

  loadFollowers = async (page?: number) => {
    const response = await this.apiClient.GET('/api/profiles/followers', {
      params: {
        query: { page: page || 1 },
      },
    });
    this.followers = [
      ...this.followers.slice(0, ((page || 1) - 1) * 20),
      ...((response.data as any)?.profiles || []),
    ];
    return {
      numLoadedProfiles: ((response.data as any)?.profiles || []).length,
      numTotalProfiles: (response.data as any)?.num_total_profiles,
    };
  };

  loadTopClips = async () => {
    const { data } = await this.apiClient.GET('/api/profiles/top_clips', {
      params: {
        query: {},
      },
    });

    if (!data) return undefined;

    this.root.clips.updateClips(data.clips);

    return data.clips;
  };

  addToLibrary = (clipId: string) => {
    if (!this.clipIds.includes(clipId)) {
      const index = this.trashedClipIndices.get(clipId);
      if (typeof index === 'number') {
        this.clipIds.splice(index, 0, clipId);
        this.trashedClipIndices.delete(clipId);
      } else {
        this.clipIds.push(clipId);
      }
    }
  };

  loadLikedPlaylists = async (pageIndex: number) => {
    const { data } = await this.apiClient.GET('/api/playlist/liked_playlist', {
      params: {
        query: { from_index: pageSize * pageIndex, page_size: pageSize },
      },
    });

    if (!data) return [];
    return data.playlists;
  };
}
