import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import {
  createPlaylist,
  getPlaylist,
  updatePlaylist,
  deletePlaylist,
  listPlaylists,
  addCollaborator,
  removeCollaborator,
  updateCollaboratorPermission,
  addTrackToPlaylist,
  getCollaborators,
  createShareLink,
  reorderPlaylistTracks,
  PlaylistCollaborator,
  AddCollaboratorRequest,
  UpdateCollaboratorPermissionRequest,
} from '../services/playlistService';
import type {
  Playlist,
  PlaylistTrack,
  CreatePlaylistRequest,
  UpdatePlaylistRequest,
} from '../types';
import { TokenStorage } from '@/services/tokenStorage';
import { useUser } from '@/features/auth/hooks/useUser';

// Create Playlist
// Action 4.4.1.5: Add optimistic update
export function useCreatePlaylist() {
  const queryClient = useQueryClient();
  const { data: user } = useUser();

  return useMutation({
    mutationFn: (data: CreatePlaylistRequest) => createPlaylist(data),
    onMutate: async (variables: CreatePlaylistRequest) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['playlists'] });

      // Snapshot previous value
      const previousPlaylists = queryClient.getQueryData<Playlist[]>(['playlists']);

      // Optimistically add new playlist to array
      const optimisticPlaylist: Playlist = {
        id: `temp-${Date.now()}`,
        user_id: user?.id || '',
        title: variables.title,
        description: variables.description,
        is_public: variables.is_public ?? true,
        cover_url: variables.cover_url,
        track_count: 0,
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
        tracks: [],
      };

      queryClient.setQueryData<Playlist[]>(['playlists'], (old) =>
        old ? [optimisticPlaylist, ...old] : [optimisticPlaylist],
      );

      return { previousPlaylists };
    },
    onError: (_error, _variables, context) => {
      // Rollback on error
      if (context?.previousPlaylists) {
        queryClient.setQueryData(['playlists'], context.previousPlaylists);
      }
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['playlists'] });
    },
  });
}

// Get Playlist
export function usePlaylist(id: string) {
  return useQuery({
    queryKey: ['playlist', id],
    queryFn: () => getPlaylist(id),
    enabled: !!id,
  });
}

// Update Playlist
// Action 4.4.1.5: Add optimistic update
export function useUpdatePlaylist() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({ id, data }: { id: string; data: UpdatePlaylistRequest }) =>
      updatePlaylist(id, data),
    onMutate: async (variables: { id: string; data: UpdatePlaylistRequest }) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['playlist', variables.id] });
      await queryClient.cancelQueries({ queryKey: ['playlists'] });

      // Snapshot previous values
      const previousPlaylist = queryClient.getQueryData<Playlist>([
        'playlist',
        variables.id,
      ]);
      const previousPlaylists = queryClient.getQueryData<Playlist[]>([
        'playlists',
      ]);

      // Optimistically update single playlist
      if (previousPlaylist) {
        queryClient.setQueryData<Playlist>(['playlist', variables.id], {
          ...previousPlaylist,
          ...variables.data,
          updated_at: new Date().toISOString(),
        });
      }

      // Optimistically update playlists array
      if (previousPlaylists) {
        queryClient.setQueryData<Playlist[]>(['playlists'], (old) =>
          old?.map((p) =>
            p.id === variables.id
              ? { ...p, ...variables.data, updated_at: new Date().toISOString() }
              : p,
          ),
        );
      }

      return { previousPlaylist, previousPlaylists };
    },
    onError: (_error, variables, context) => {
      // Rollback on error
      if (context?.previousPlaylist) {
        queryClient.setQueryData(['playlist', variables.id], context.previousPlaylist);
      }
      if (context?.previousPlaylists) {
        queryClient.setQueryData(['playlists'], context.previousPlaylists);
      }
    },
    onSuccess: (data: Playlist) => {
      queryClient.invalidateQueries({ queryKey: ['playlist', data.id] });
      queryClient.invalidateQueries({ queryKey: ['playlists'] });
    },
  });
}

// Delete Playlist
// Action 4.4.1.5: Add optimistic update
export function useDeletePlaylist() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (id: string) => deletePlaylist(id),
    onMutate: async (id: string) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['playlist', id] });
      await queryClient.cancelQueries({ queryKey: ['playlists'] });

      // Snapshot previous values
      const previousPlaylist = queryClient.getQueryData<Playlist>([
        'playlist',
        id,
      ]);
      const previousPlaylists = queryClient.getQueryData<Playlist[]>([
        'playlists',
      ]);

      // Optimistically remove from playlists array
      if (previousPlaylists) {
        queryClient.setQueryData<Playlist[]>(['playlists'], (old) =>
          old?.filter((p) => p.id !== id),
        );
      }

      // Remove single playlist query
      queryClient.removeQueries({ queryKey: ['playlist', id] });

      return { previousPlaylist, previousPlaylists };
    },
    onError: (_error, id, context) => {
      // Rollback on error
      if (context?.previousPlaylist) {
        queryClient.setQueryData(['playlist', id], context.previousPlaylist);
      }
      if (context?.previousPlaylists) {
        queryClient.setQueryData(['playlists'], context.previousPlaylists);
      }
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['playlists'] });
    },
  });
}

// List Playlists
export function usePlaylists(
  limit = 20,
  offset = 0,
  sortBy?: 'created_at' | 'title' | 'track_count',
  sortOrder?: 'asc' | 'desc',
) {
  // Check if token exists in storage (more reliable than waiting for store hydration)
  const hasToken = !!TokenStorage.getAccessToken();
  const page = Math.floor(offset / limit) + 1;

  return useQuery({
    queryKey: ['playlists', limit, offset, sortBy, sortOrder],
    queryFn: () => listPlaylists(page, limit, undefined, sortBy, sortOrder),
    enabled: hasToken, // Only fetch if token exists
  });
}

// Add Collaborator
// Action 4.4.1.5: Add optimistic update
export function useAddCollaborator() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({
      playlistId,
      data,
    }: {
      playlistId: string;
      data: AddCollaboratorRequest;
    }) => addCollaborator(playlistId, data),
    onMutate: async (variables: {
      playlistId: string;
      data: AddCollaboratorRequest;
    }) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId, 'collaborators'],
      });
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId],
      });

      // Snapshot previous values
      const previousCollaborators = queryClient.getQueryData<
        PlaylistCollaborator[]
      >(['playlist', variables.playlistId, 'collaborators']);
      const previousPlaylist = queryClient.getQueryData<Playlist>([
        'playlist',
        variables.playlistId,
      ]);

      // Optimistically add collaborator
      const optimisticCollaborator: PlaylistCollaborator = {
        id: `temp-${Date.now()}`,
        playlist_id: variables.playlistId,
        user_id: variables.data.user_id,
        permission: variables.data.permission,
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
        user: {
          id: variables.data.user_id,
          username: '', // Will be filled by server response
          email: '',
        },
      };

      if (previousCollaborators) {
        queryClient.setQueryData<PlaylistCollaborator[]>(
          ['playlist', variables.playlistId, 'collaborators'],
          [...previousCollaborators, optimisticCollaborator],
        );
      }

      return { previousCollaborators, previousPlaylist };
    },
    onError: (_error, variables, context) => {
      // Rollback on error
      if (context?.previousCollaborators) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId, 'collaborators'],
          context.previousCollaborators,
        );
      }
      if (context?.previousPlaylist) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId],
          context.previousPlaylist,
        );
      }
    },
    onSuccess: (data: PlaylistCollaborator) => {
      queryClient.invalidateQueries({
        queryKey: ['playlist', data.playlist_id],
      });
      queryClient.invalidateQueries({
        queryKey: ['playlist', data.playlist_id, 'collaborators'],
      });
      queryClient.invalidateQueries({ queryKey: ['playlists'] });
    },
  });
}

// Remove Collaborator
// Action 4.4.1.5: Add optimistic update
export function useRemoveCollaborator() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({
      playlistId,
      userId,
    }: {
      playlistId: string;
      userId: string;
    }) => removeCollaborator(playlistId, userId),
    onMutate: async (variables: { playlistId: string; userId: string }) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId, 'collaborators'],
      });
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId],
      });

      // Snapshot previous values
      const previousCollaborators = queryClient.getQueryData<
        PlaylistCollaborator[]
      >(['playlist', variables.playlistId, 'collaborators']);
      const previousPlaylist = queryClient.getQueryData<Playlist>([
        'playlist',
        variables.playlistId,
      ]);

      // Optimistically remove collaborator
      if (previousCollaborators) {
        queryClient.setQueryData<PlaylistCollaborator[]>(
          ['playlist', variables.playlistId, 'collaborators'],
          previousCollaborators.filter((c) => c.user_id !== variables.userId),
        );
      }

      return { previousCollaborators, previousPlaylist };
    },
    onError: (_error, variables, context) => {
      // Rollback on error
      if (context?.previousCollaborators) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId, 'collaborators'],
          context.previousCollaborators,
        );
      }
      if (context?.previousPlaylist) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId],
          context.previousPlaylist,
        );
      }
    },
    onSuccess: (_, variables) => {
      queryClient.invalidateQueries({
        queryKey: ['playlist', variables.playlistId],
      });
      queryClient.invalidateQueries({
        queryKey: ['playlist', variables.playlistId, 'collaborators'],
      });
      queryClient.invalidateQueries({ queryKey: ['playlists'] });
    },
  });
}

// Update Collaborator Permission
// Action 4.4.1.5: Add optimistic update
export function useUpdateCollaboratorPermission() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({
      playlistId,
      userId,
      data,
    }: {
      playlistId: string;
      userId: string;
      data: UpdateCollaboratorPermissionRequest;
    }) => updateCollaboratorPermission(playlistId, userId, data),
    onMutate: async (variables: {
      playlistId: string;
      userId: string;
      data: UpdateCollaboratorPermissionRequest;
    }) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId, 'collaborators'],
      });
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId],
      });

      // Snapshot previous values
      const previousCollaborators = queryClient.getQueryData<
        PlaylistCollaborator[]
      >(['playlist', variables.playlistId, 'collaborators']);
      const previousPlaylist = queryClient.getQueryData<Playlist>([
        'playlist',
        variables.playlistId,
      ]);

      // Optimistically update collaborator permission
      if (previousCollaborators) {
        queryClient.setQueryData<PlaylistCollaborator[]>(
          ['playlist', variables.playlistId, 'collaborators'],
          previousCollaborators.map((c) =>
            c.user_id === variables.userId
              ? { ...c, permission: variables.data.permission }
              : c,
          ),
        );
      }

      return { previousCollaborators, previousPlaylist };
    },
    onError: (_error, variables, context) => {
      // Rollback on error
      if (context?.previousCollaborators) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId, 'collaborators'],
          context.previousCollaborators,
        );
      }
      if (context?.previousPlaylist) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId],
          context.previousPlaylist,
        );
      }
    },
    onSuccess: (_, variables) => {
      queryClient.invalidateQueries({
        queryKey: ['playlist', variables.playlistId],
      });
      queryClient.invalidateQueries({
        queryKey: ['playlist', variables.playlistId, 'collaborators'],
      });
      queryClient.invalidateQueries({ queryKey: ['playlists'] });
    },
  });
}

// Create Share Link
export function useCreateShareLink() {
  return useMutation({
    mutationFn: (id: string) => createShareLink(id),
  });
}

// Reorder Playlist Tracks
// Action 4.4.1.5: Add optimistic update
export function useReorderPlaylistTracks() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({
      playlistId,
      trackIds,
    }: {
      playlistId: string;
      trackIds: string[];
    }) => reorderPlaylistTracks(playlistId, { track_ids: trackIds }),
    onMutate: async (variables: {
      playlistId: string;
      trackIds: string[];
    }) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId],
      });

      // Snapshot previous value
      const previousPlaylist = queryClient.getQueryData<Playlist>([
        'playlist',
        variables.playlistId,
      ]);

      // Optimistically reorder tracks
      if (previousPlaylist && previousPlaylist.tracks) {
        // Create a map of track IDs to track objects
        const trackMap = new Map(
          previousPlaylist.tracks.map((t) => [t.track_id, t]),
        );

        // Reorder tracks based on trackIds array
        const reorderedTracks = variables.trackIds
          .map((trackId, index) => {
            const track = trackMap.get(trackId);
            return track
              ? { ...track, position: index + 1 }
              : undefined;
          })
          .filter((t): t is PlaylistTrack => t !== undefined);

        queryClient.setQueryData<Playlist>(
          ['playlist', variables.playlistId],
          {
            ...previousPlaylist,
            tracks: reorderedTracks,
            updated_at: new Date().toISOString(),
          },
        );
      }

      return { previousPlaylist };
    },
    onError: (_error, variables, context) => {
      // Rollback on error
      if (context?.previousPlaylist) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId],
          context.previousPlaylist,
        );
      }
    },
    onSuccess: (_, variables) => {
      queryClient.invalidateQueries({
        queryKey: ['playlist', variables.playlistId],
      });
    },
  });
}

// Get Collaborators
export function useCollaborators(playlistId: string) {
  return useQuery({
    queryKey: ['playlist', playlistId, 'collaborators'],
    queryFn: () => getCollaborators(playlistId),
    enabled: !!playlistId,
  });
}

// Add Track to Playlist
// Action 4.4.1.5: Add optimistic update
export function useAddTrackToPlaylist() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({
      playlistId,
      trackId,
    }: {
      playlistId: string;
      trackId: string;
    }) => addTrackToPlaylist(playlistId, trackId),
    onMutate: async (variables: { playlistId: string; trackId: string }) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({
        queryKey: ['playlist', variables.playlistId],
      });
      await queryClient.cancelQueries({ queryKey: ['playlists'] });

      // Snapshot previous values
      const previousPlaylist = queryClient.getQueryData<Playlist>([
        'playlist',
        variables.playlistId,
      ]);
      const previousPlaylists = queryClient.getQueryData<Playlist[]>([
        'playlists',
      ]);

      // Optimistically add track to playlist
      if (previousPlaylist) {
        const optimisticTrack: PlaylistTrack = {
          id: `temp-${Date.now()}`,
          playlist_id: variables.playlistId,
          track_id: variables.trackId,
          position: (previousPlaylist.tracks?.length || 0) + 1,
          added_at: new Date().toISOString(),
        };

        queryClient.setQueryData<Playlist>(
          ['playlist', variables.playlistId],
          {
            ...previousPlaylist,
            tracks: [...(previousPlaylist.tracks || []), optimisticTrack],
            track_count: (previousPlaylist.track_count || 0) + 1,
            updated_at: new Date().toISOString(),
          },
        );
      }

      // Optimistically update track count in playlists array
      if (previousPlaylists) {
        queryClient.setQueryData<Playlist[]>(['playlists'], (old) =>
          old?.map((p) =>
            p.id === variables.playlistId
              ? {
                  ...p,
                  track_count: (p.track_count || 0) + 1,
                  updated_at: new Date().toISOString(),
                }
              : p,
          ),
        );
      }

      return { previousPlaylist, previousPlaylists };
    },
    onError: (_error, variables, context) => {
      // Rollback on error
      if (context?.previousPlaylist) {
        queryClient.setQueryData(
          ['playlist', variables.playlistId],
          context.previousPlaylist,
        );
      }
      if (context?.previousPlaylists) {
        queryClient.setQueryData(['playlists'], context.previousPlaylists);
      }
    },
    onSuccess: (_, variables) => {
      queryClient.invalidateQueries({
        queryKey: ['playlist', variables.playlistId],
      });
      queryClient.invalidateQueries({ queryKey: ['playlists'] }); // Update track counts in list
    },
  });
}
