"use client"

import { createContext, useContext, useState, useCallback, useEffect, useRef, type ReactNode } from "react"
import { useAuth } from "@/components/auth-provider"

export interface UserPlaylist {
  id: string
  title: string
  description?: string
  image: string
  tracks: any[]
  createdAt: Date
  is_public: boolean
}

interface HistoryItem {
  track: any
  playedAt: Date
}

interface UserContextType {
  playlists: UserPlaylist[]
  createPlaylist: (title: string, description?: string) => Promise<UserPlaylist | null>
  deletePlaylist: (id: string) => Promise<void>
  addTrackToPlaylist: (playlistId: string, track: any) => Promise<any>
  removeTrackFromPlaylist: (playlistId: string, trackId: string) => Promise<void>
  updatePlaylist: (id: string, data: Partial<UserPlaylist>) => void
  followedArtists: Set<string>
  toggleFollowArtist: (artistId: string) => Promise<boolean>
  isFollowingArtist: (artistId: string) => boolean
  loadFollowedArtists: () => Promise<void>
  getFollowedArtists: () => { id: string; name: string; image: string; followers: number }[]
  addToHistory: (track: any) => Promise<void>
  listeningHistory: HistoryItem[]
  clearHistory: () => Promise<void>
  refreshPlaylists: () => Promise<void>
  settings: {
    streamingQuality: "low" | "normal" | "high" | "very_high"
    downloadQuality: "low" | "normal" | "high" | "very_high"
    autoplay: boolean
    dataSaver: boolean
    explicitContent: boolean
    privateSession: boolean
  }
  updateSettings: (settings: Partial<UserContextType["settings"]>) => void
}

const UserContext = createContext<UserContextType | undefined>(undefined)

async function fetchWithRetry(url: string, options?: RequestInit, retries = 3): Promise<Response> {
  for (let i = 0; i < retries; i++) {
    const response = await fetch(url, options)
    if (response.status === 429) {
      await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)))
      continue
    }
    return response
  }
  throw new Error("Too many requests")
}

export function UserProvider({ children }: { children: ReactNode }) {
  const { user } = useAuth()
  const [playlists, setPlaylists] = useState<UserPlaylist[]>([])
  const [followedArtists, setFollowedArtists] = useState<Set<string>>(new Set())
  const [followedArtistsData, setFollowedArtistsData] = useState<
    { id: string; name: string; image: string; followers: number }[]
  >([])
  const [listeningHistory, setListeningHistory] = useState<HistoryItem[]>([])
  const [settings, setSettings] = useState({
    streamingQuality: "high" as const,
    downloadQuality: "very_high" as const,
    autoplay: true,
    dataSaver: false,
    explicitContent: true,
    privateSession: false,
  })

  const isLoadingRef = useRef(false)
  const lastLoadedUserIdRef = useRef<string | null>(null)

  const refreshPlaylists = useCallback(async () => {
    if (!user?.id) return

    try {
      const playlistsResponse = await fetch(`/api/users/${user.id}/playlists?current_user_id=${user.id}`)
      if (playlistsResponse.ok) {
        const data = await playlistsResponse.json()
        const playlistsArray = data.data?.playlists || data.playlists || []
        setPlaylists(
          playlistsArray.map((p: any) => ({
            id: p.id,
            title: p.name,
            description: p.description,
            image: p.cover_url || "/music-playlist.png",
            is_public: p.is_public,
            tracks: [],
            createdAt: new Date(p.created_at),
          })),
        )
      }
    } catch (error) {
      console.error("Error refreshing playlists:", error)
    }
  }, [user?.id])

  const loadHistory = useCallback(async () => {
    if (!user?.id) return

    try {
      const response = await fetchWithRetry(`/api/users/${user.id}/history`)
      if (response.ok) {
        const data = await response.json()
        setListeningHistory(
          (data.data || []).map((item: any) => ({
            track: {
              id: item.track_id,
              title: item.track_title || item.title,
              artist: item.artist_name || item.artist,
              image: item.cover_url || item.image || "/placeholder.svg",
              duration: item.duration || 0,
            },
            playedAt: new Date(item.played_at || item.created_at),
          })),
        )
      }
    } catch (error) {
      console.error("Error loading history:", error)
    }
  }, [user?.id])

  const loadFollowedArtists = useCallback(async () => {
    if (!user?.id) return

    try {
      const followingResponse = await fetchWithRetry(`/api/users/${user.id}/following`)
      if (followingResponse.ok) {
        const data = await followingResponse.json()
        const artistIds = (data.data || []).map((a: any) => a.artist_id || a.id)
        setFollowedArtists(new Set(artistIds))
        setFollowedArtistsData(
          (data.data || []).map((a: any) => ({
            id: a.artist_id || a.id,
            name: a.artist_name || a.name || "Unknown Artist",
            image: a.artist_image || a.image || "/placeholder.svg",
            followers: a.followers || 0,
          })),
        )
      }
    } catch (error) {
      console.error("Error loading followed artists:", error)
    }
  }, [user?.id])

  useEffect(() => {
    const loadUserData = async () => {
      if (!user?.id) return
      if (isLoadingRef.current) return
      if (lastLoadedUserIdRef.current === user.id) return

      isLoadingRef.current = true
      lastLoadedUserIdRef.current = user.id

      try {
        await loadFollowedArtists()
      } catch (error) {
        console.error("[v0] Error loading followed artists:", error)
      }

      await new Promise((resolve) => setTimeout(resolve, 100))

      try {
        const playlistsResponse = await fetchWithRetry(`/api/users/${user.id}/playlists?current_user_id=${user.id}`)
        if (playlistsResponse.ok) {
          const data = await playlistsResponse.json()
          const playlistsArray = data.data?.playlists || data.playlists || []
          setPlaylists(
            playlistsArray.map((p: any) => ({
              id: p.id,
              title: p.name,
              description: p.description,
              image: p.cover_url || "/music-playlist.png",
              is_public: p.is_public,
              tracks: [],
              createdAt: new Date(p.created_at),
            })),
          )
        } else {
          console.error("[v0] Failed to load playlists:", playlistsResponse.status)
        }
      } catch (error) {
        console.error("[v0] Error loading playlists:", error)
        // Set empty playlists on error
        setPlaylists([])
      }

      await new Promise((resolve) => setTimeout(resolve, 100))

      try {
        await loadHistory()
      } catch (error) {
        console.error("[v0] Error loading history:", error)
      }

      isLoadingRef.current = false
    }

    loadUserData()
  }, [user?.id, loadHistory, loadFollowedArtists])

  useEffect(() => {
    if (!user?.id) {
      setFollowedArtists(new Set())
      setFollowedArtistsData([])
      setPlaylists([])
      setListeningHistory([])
      lastLoadedUserIdRef.current = null
    }
  }, [user?.id])

  const createPlaylist = useCallback(
    async (title: string, description?: string): Promise<UserPlaylist | null> => {
      if (!user?.id) return null

      try {
        const response = await fetchWithRetry("/api/playlists", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            name: title,
            description,
            user_id: user.id,
            is_public: false,
          }),
        })

        if (response.ok) {
          const data = await response.json()
          const newPlaylist: UserPlaylist = {
            id: data.data.id,
            title: data.data.name,
            description: data.data.description,
            image: "/music-playlist-concept.png",
            tracks: [],
            createdAt: new Date(),
            is_public: false,
          }
          setPlaylists((prev) => [newPlaylist, ...prev])
          return newPlaylist
        }
      } catch (error) {
        console.error("Error creating playlist:", error)
      }
      return null
    },
    [user?.id],
  )

  const deletePlaylist = useCallback(async (id: string) => {
    try {
      const response = await fetchWithRetry(`/api/playlists/${id}`, {
        method: "DELETE",
      })

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}))
        throw new Error(errorData.error || "Failed to delete playlist")
      }

      setPlaylists((prev) => prev.filter((p) => p.id !== id))
    } catch (error) {
      console.error("Error deleting playlist:", error)
      throw error
    }
  }, [])

  const addTrackToPlaylist = useCallback(async (playlistId: string, track: any) => {
    const trackId = track?.id

    if (!playlistId || !trackId) {
      throw new Error("Playlist ID and Track ID are required")
    }

    // Update local state
    setPlaylists((prev) =>
      prev.map((p) => {
        if (p.id === playlistId && !p.tracks.find((t) => t.id === trackId)) {
          return {
            ...p,
            tracks: [...p.tracks, track],
            image: p.tracks.length === 0 ? track.image || track.cover_url : p.image,
          }
        }
        return p
      }),
    )

    return { success: true }
  }, [])

  const removeTrackFromPlaylist = useCallback(async (playlistId: string, trackId: string) => {
    if (!playlistId || !trackId) {
      console.error("Invalid playlistId or trackId")
      return
    }

    try {
      const response = await fetchWithRetry(`/api/playlists/${playlistId}/tracks?trackId=${trackId}`, {
        method: "DELETE",
      })

      if (response.ok) {
        setPlaylists((prev) =>
          prev.map((p) => {
            if (p.id === playlistId) {
              return { ...p, tracks: p.tracks.filter((t) => t.id !== trackId) }
            }
            return p
          }),
        )
      }
    } catch (error) {
      console.error("Error removing track from playlist:", error)
    }
  }, [])

  const updatePlaylist = useCallback((id: string, data: Partial<UserPlaylist>) => {
    setPlaylists((prev) => prev.map((p) => (p.id === id ? { ...p, ...data } : p)))
  }, [])

  const toggleFollowArtist = useCallback(
    async (artistId: string): Promise<boolean> => {
      if (!user?.id) {
        console.error("User not logged in")
        return false
      }

      const isCurrentlyFollowing = followedArtists.has(artistId)

      try {
        let response: Response

        if (isCurrentlyFollowing) {
          response = await fetch(`/api/artists/${artistId}/follow?userId=${user.id}`, {
            method: "DELETE",
          })
        } else {
          response = await fetch(`/api/artists/${artistId}/follow`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ userId: user.id }),
          })
        }

        if (!response.ok) {
          const errorData = await response.json()
          throw new Error(errorData.error || "Failed to update follow status")
        }

        const data = await response.json()

        setFollowedArtists((prev) => {
          const next = new Set(prev)
          if (data.following) {
            next.add(artistId)
          } else {
            next.delete(artistId)
          }
          return next
        })

        return data.following
      } catch (error) {
        console.error("Error toggling follow artist:", error)
        throw error
      }
    },
    [user?.id, followedArtists],
  )

  const isFollowingArtist = useCallback((artistId: string) => followedArtists.has(artistId), [followedArtists])

  const addToHistory = useCallback(
    async (track: any) => {
      if (!user?.id || !track?.id) return

      try {
        await fetchWithRetry(`/api/users/${user.id}/history`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            trackId: track.id,
          }),
        })
        setListeningHistory((prev) => [
          { track, playedAt: new Date() },
          ...prev.filter((item) => item.track.id !== track.id),
        ])
      } catch (error) {
        console.error("Error adding to history:", error)
      }
    },
    [user?.id],
  )

  const clearHistory = useCallback(async () => {
    if (!user?.id) return

    try {
      await fetchWithRetry(`/api/users/${user.id}/history`, {
        method: "DELETE",
      })
      setListeningHistory([])
    } catch (error) {
      console.error("Error clearing history:", error)
    }
  }, [user?.id])

  const getFollowedArtists = useCallback(() => {
    return followedArtistsData
  }, [followedArtistsData])

  const updateSettings = useCallback((newSettings: Partial<UserContextType["settings"]>) => {
    setSettings((prevSettings) => {
      const updated = { ...prevSettings, ...newSettings }

      if ("streamingQuality" in newSettings || "dataSaver" in newSettings || "downloadQuality" in newSettings) {
        // Dispatch custom event to notify player
        if (typeof window !== "undefined") {
          window.dispatchEvent(
            new CustomEvent("streamingSettingsChanged", {
              detail: {
                streamingQuality: updated.streamingQuality,
                dataSaver: updated.dataSaver,
              },
            }),
          )
        }
      }

      return updated
    })
  }, [])

  return (
    <UserContext.Provider
      value={{
        playlists,
        createPlaylist,
        deletePlaylist,
        addTrackToPlaylist,
        removeTrackFromPlaylist,
        updatePlaylist,
        followedArtists,
        toggleFollowArtist,
        isFollowingArtist,
        loadFollowedArtists,
        getFollowedArtists,
        addToHistory,
        listeningHistory,
        clearHistory,
        refreshPlaylists,
        settings,
        updateSettings,
      }}
    >
      {children}
    </UserContext.Provider>
  )
}

export function useUser() {
  const context = useContext(UserContext)
  if (context === undefined) {
    throw new Error("useUser must be used within a UserProvider")
  }
  return context
}
