import Foundation

struct LyricsSearchResult: Sendable {
    let title: String
    let artist: String
    let lyricsES: String
    let albumArtURL: String?
    let appleMusicURL: String?
}

actor LyricsSearchService {

    // MARK: - Public

    func searchLyrics(artist: String, title: String) async throws -> [LyricsSearchResult] {
        async let lrcResults = searchLRCLIB(artist: artist, title: title)
        async let itunesResults = searchITunes(artist: artist, title: title)

        let lyrics = try await lrcResults
        let metadata = try? await itunesResults

        return lyrics.map { lrc in
            let match = metadata?.bestMatch(artist: lrc.artistName, title: lrc.trackName)
            return LyricsSearchResult(
                title: lrc.trackName,
                artist: lrc.artistName,
                lyricsES: lrc.plainLyrics,
                albumArtURL: match?.artworkURL600,
                appleMusicURL: match?.trackViewURL
            )
        }
    }

    // MARK: - LRCLIB

    private struct LRCLIBResult: Decodable, Sendable {
        let trackName: String
        let artistName: String
        let plainLyrics: String

        enum CodingKeys: String, CodingKey {
            case trackName, artistName, plainLyrics
        }

        init(from decoder: Decoder) throws {
            let c = try decoder.container(keyedBy: CodingKeys.self)
            trackName = try c.decode(String.self, forKey: .trackName)
            artistName = try c.decode(String.self, forKey: .artistName)
            plainLyrics = (try? c.decode(String.self, forKey: .plainLyrics)) ?? ""
        }
    }

    private func searchLRCLIB(artist: String, title: String) async throws -> [LRCLIBResult] {
        var components = URLComponents(string: "https://lrclib.net/api/search")!
        components.queryItems = [
            URLQueryItem(name: "track_name", value: title),
            URLQueryItem(name: "artist_name", value: artist),
        ]
        guard let url = components.url else { return [] }

        var request = URLRequest(url: url)
        request.setValue("Conjuga/1.0", forHTTPHeaderField: "User-Agent")

        let (data, response) = try await URLSession.shared.data(for: request)
        guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { return [] }

        let results = try JSONDecoder().decode([LRCLIBResult].self, from: data)
        return results.filter { !$0.plainLyrics.isEmpty }
    }

    // MARK: - iTunes Search

    private struct ITunesResponse: Decodable {
        let results: [ITunesTrack]
    }

    private struct ITunesTrack: Decodable {
        let trackName: String?
        let artistName: String?
        let artworkUrl100: String?
        let trackViewUrl: String?

        var artworkURL600: String? {
            artworkUrl100?.replacingOccurrences(of: "100x100", with: "600x600")
        }

        var trackViewURL: String? { trackViewUrl }
    }

    private struct ITunesMetadata: Sendable {
        let tracks: [ITunesTrack]

        func bestMatch(artist: String, title: String) -> ITunesTrack? {
            let normalizedArtist = artist.lowercased()
            let normalizedTitle = title.lowercased()

            // Prefer exact title+artist match, then just title
            return tracks.first {
                ($0.trackName ?? "").lowercased().contains(normalizedTitle) &&
                ($0.artistName ?? "").lowercased().contains(normalizedArtist)
            } ?? tracks.first {
                ($0.trackName ?? "").lowercased().contains(normalizedTitle)
            } ?? tracks.first
        }
    }

    private func searchITunes(artist: String, title: String) async throws -> ITunesMetadata {
        let query = "\(artist) \(title)"
        var components = URLComponents(string: "https://itunes.apple.com/search")!
        components.queryItems = [
            URLQueryItem(name: "term", value: query),
            URLQueryItem(name: "media", value: "music"),
            URLQueryItem(name: "limit", value: "5"),
        ]
        guard let url = components.url else { return ITunesMetadata(tracks: []) }

        let (data, _) = try await URLSession.shared.data(from: url)
        let response = try JSONDecoder().decode(ITunesResponse.self, from: data)
        return ITunesMetadata(tracks: response.results)
    }
}
