import { Server as SocketServer } from 'socket.io'

interface TrackMetadata {
  track_name: string
  artist_name: string
  release_name?: string
  mbid?: string
  additional_info?: {
    recording_mbid?: string
    artist_mbids?: string[]
    release_mbid?: string
  }
}

interface LastFmImage {
  size: string
  '#text': string
}

interface LastFmAlbum {
  image?: LastFmImage[]
}

interface LastFmTrack {
  album?: LastFmAlbum
}

interface LastFmResponse {
  album?: LastFmAlbum
  track?: LastFmTrack
}

interface NowPlayingData {
  track_name?: string
  artist_name?: string
  release_name?: string
  mbid?: string
  coverArt?: string | null
  lastFmData?: LastFmResponse
  status: 'loading' | 'partial' | 'complete' | 'error'
  message?: string
}

export class NowPlayingService {
  private readonly io: SocketServer
  private readonly lastFmApiKey: string | undefined

  constructor(io: SocketServer) {
    this.io = io
    this.lastFmApiKey = process.env.LASTFM_API_KEY
  }

  async fetchNowPlaying(socketId: string) {
    const emit = (data: Partial<NowPlayingData>) => {
      this.io.to(socketId).emit('nowPlaying', data)
    }

    try {
      emit({ status: 'loading', message: 'Fetching from ListenBrainz...' })
      
      const listenBrainzResponse = await fetch(
        'https://api.listenbrainz.org/1/user/p0ntus/playing-now',
        {
          headers: process.env.LISTENBRAINZ_TOKEN 
            ? { Authorization: `Token ${process.env.LISTENBRAINZ_TOKEN}` }
            : {}
        }
      )

      if (!listenBrainzResponse.ok) {
        emit({ 
          status: 'error',
          message: `ListenBrainz error: ${listenBrainzResponse.status}`
        })
        return
      }

      const listenBrainzData = await listenBrainzResponse.json()

      if (listenBrainzData.payload.count === 0) {
        emit({ 
          status: 'complete',
          message: 'No track currently playing'
        })
        return
      }

      const trackMetadata: TrackMetadata = listenBrainzData.payload.listens[0].track_metadata
      
      emit({
        status: 'partial',
        track_name: trackMetadata.track_name,
        artist_name: trackMetadata.artist_name,
        release_name: trackMetadata.release_name,
        mbid: trackMetadata.additional_info?.release_mbid,
        message: 'Fetching additional info...'
      })

      // Try to get data from Last.fm if MBID
      let lastFmData: LastFmResponse | null = null
      let lastFmCoverArt: string | null = null
      
      if (this.lastFmApiKey) {
        emit({ status: 'partial', message: 'Fetching from Last.fm...' })
        
        const lastFmQueries = []
        
        // Try with MBID if available
        if (trackMetadata.additional_info?.recording_mbid) {
          lastFmQueries.push(this.fetchLastFmByMbid(trackMetadata.additional_info.recording_mbid))
        }
        
        // Also try with track and artist name
        lastFmQueries.push(this.fetchLastFmByTrack(
          trackMetadata.artist_name,
          trackMetadata.track_name
        ))

        const lastFmResults = await Promise.allSettled(lastFmQueries)
        
        for (const result of lastFmResults) {
          if (result.status === 'fulfilled' && result.value) {
            lastFmData = result.value
            // Extract cover
            if (lastFmData.album?.image) {
              const images = lastFmData.album.image
              const largeImage = images.find((img: LastFmImage) => img.size === 'extralarge') ||
                                images.find((img: LastFmImage) => img.size === 'large') ||
                                images[images.length - 1]
              if (largeImage && largeImage['#text'] && largeImage['#text'].trim() !== '') {
                lastFmCoverArt = largeImage['#text']
              }
            } else if (lastFmData.track?.album?.image) {
              const images = lastFmData.track.album.image
              const largeImage = images.find((img: LastFmImage) => img.size === 'extralarge') ||
                               images.find((img: LastFmImage) => img.size === 'large') ||
                               images[images.length - 1]
              if (largeImage && largeImage['#text'] && largeImage['#text'].trim() !== '') {
                lastFmCoverArt = largeImage['#text']
              }
            }
            break
          }
        }
      }

      // Get album art
      let finalCoverArt = lastFmCoverArt
      
      if (!finalCoverArt) {
        if (trackMetadata.additional_info?.release_mbid) {
          emit({ status: 'partial', message: 'Fetching from Cover Art Archive...' })
          
          try {
            const coverArtResponse = await fetch(
              `https://coverartarchive.org/release/${trackMetadata.additional_info.release_mbid}/front`
            )
            
            if (coverArtResponse.ok) {
              finalCoverArt = coverArtResponse.url
            }
          } catch (error) {
            console.log('[!] Cover Art Archive direct fetch failed:', error)
          }
        }
        
        if (!finalCoverArt && trackMetadata.release_name && trackMetadata.artist_name) {
          emit({ status: 'partial', message: 'Searching MusicBrainz for album art...' })
          
          try {
            const mbSearchResponse = await fetch(
              `https://musicbrainz.org/ws/2/release/?query=artist:${encodeURIComponent(
                trackMetadata.artist_name
              )}%20AND%20release:${encodeURIComponent(trackMetadata.release_name)}&fmt=json&limit=1`
            )
            
            if (mbSearchResponse.ok) {
              const mbData = await mbSearchResponse.json()
              
              if (mbData.releases && mbData.releases.length > 0) {
                const releaseMbid = mbData.releases[0].id
                
                try {
                  const coverArtResponse = await fetch(
                    `https://coverartarchive.org/release/${releaseMbid}/front`
                  )
                  
                  if (coverArtResponse.ok) {
                    finalCoverArt = coverArtResponse.url
                  }
                } catch (error) {
                  console.log('[!] Cover Art Archive fallback fetch failed:', error)
                }
              }
            }
          } catch (error) {
            console.log('[!] MusicBrainz search failed:', error)
          }
        }
      }

      emit({
        status: 'complete',
        track_name: trackMetadata.track_name,
        artist_name: trackMetadata.artist_name,
        release_name: trackMetadata.release_name,
        mbid: trackMetadata.additional_info?.release_mbid || trackMetadata.mbid,
        coverArt: finalCoverArt || null,
        lastFmData: lastFmData || undefined,
        message: 'Complete'
      })
    } catch (error) {
      console.error('[!] Error in fetchNowPlaying:', error)
      emit({
        status: 'error',
        message: error instanceof Error ? error.message : 'Unknown error occurred'
      })
    }
  }

  private async fetchLastFmByMbid(mbid: string): Promise<LastFmResponse | null> {
    if (!this.lastFmApiKey) return null
    
    try {
      const response = await fetch(
        `https://ws.audioscrobbler.com/2.0/?method=track.getInfoByMbid&mbid=${mbid}&api_key=${this.lastFmApiKey}&format=json`
      )
      
      if (response.ok) {
        return await response.json() as LastFmResponse
      }
    } catch (error) {
      console.log('[!] Last.fm MBID fetch failed:', error)
    }
    
    return null
  }

  private async fetchLastFmByTrack(artist: string, track: string): Promise<LastFmResponse | null> {
    if (!this.lastFmApiKey) return null
    
    try {
      const params = new URLSearchParams({
        method: 'track.getInfo',
        api_key: this.lastFmApiKey,
        artist: artist,
        track: track,
        format: 'json',
        autocorrect: '1'
      })
      
      const response = await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`)
      
      if (response.ok) {
        return await response.json() as LastFmResponse
      }
    } catch (error) {
      console.log('[!] Last.fm track fetch failed:', error)
    }
    
    return null
  }
}