import AVFoundation
import Combine
import MusicKit
import SwiftUI

@MainActor
class MusicService: ObservableObject {
    static let shared = MusicService()
    
    @Published var isAuthorized = false
    @Published var playlists: MusicItemCollection<Playlist> = []
    @Published var selectedPlaylistId: String? {
        didSet {
            UserDefaults.standard.set(selectedPlaylistId, forKey: "ascently_selected_playlist_id")
        }
    }
    @Published var isMusicEnabled: Bool {
        didSet {
            UserDefaults.standard.set(isMusicEnabled, forKey: "ascently_music_enabled")
            if !isMusicEnabled {
                // Genuinely unsure what I want to do with this but we should account for it at some point
            }
        }
    }
    @Published var isAutoPlayEnabled: Bool {
        didSet {
            UserDefaults.standard.set(isAutoPlayEnabled, forKey: "ascently_music_autoplay_enabled")
        }
    }
    @Published var isAutoStopEnabled: Bool {
        didSet {
            UserDefaults.standard.set(isAutoStopEnabled, forKey: "ascently_music_autostop_enabled")
        }
    }
    @Published var isPlaying = false
    
    private var cancellables = Set<AnyCancellable>()
    private var hasStartedSessionPlayback = false
    private var currentPlaylistTrackIds: Set<MusicItemID> = []
    
    private init() {
        self.selectedPlaylistId = UserDefaults.standard.string(forKey: "ascently_selected_playlist_id")
        self.isMusicEnabled = UserDefaults.standard.bool(forKey: "ascently_music_enabled")
        self.isAutoPlayEnabled = UserDefaults.standard.bool(forKey: "ascently_music_autoplay_enabled")
        self.isAutoStopEnabled = UserDefaults.standard.bool(forKey: "ascently_music_autostop_enabled")
        
        if isMusicEnabled {
            Task {
                await checkAuthorizationStatus()
            }
        }
        
        setupObservers()
    }
    
    private func setupObservers() {
        SystemMusicPlayer.shared.state.objectWillChange
            .sink { [weak self] _ in
                self?.updatePlaybackStatus()
            }
            .store(in: &cancellables)
            
        SystemMusicPlayer.shared.queue.objectWillChange
            .sink { [weak self] _ in
                self?.checkQueueConsistency()
            }
            .store(in: &cancellables)
    }
    
    private func updatePlaybackStatus() {
        Task { @MainActor [weak self] in
            self?.isPlaying = SystemMusicPlayer.shared.state.playbackStatus == .playing
        }
    }
    
    private func checkQueueConsistency() {
        guard hasStartedSessionPlayback else { return }
        
        Task { @MainActor [weak self] in
            guard let self = self else { return }
            if let currentEntry = SystemMusicPlayer.shared.queue.currentEntry,
               let item = currentEntry.item {
                if !self.currentPlaylistTrackIds.isEmpty && !self.currentPlaylistTrackIds.contains(item.id) {
                    self.hasStartedSessionPlayback = false
                }
            }
        }
    }
    
    func toggleMusicEnabled(_ enabled: Bool) {
        isMusicEnabled = enabled
        if enabled {
            Task {
                await checkAuthorizationStatus()
            }
        }
    }
    
    func checkAuthorizationStatus() async {
        let status = await MusicAuthorization.request()
        self.isAuthorized = status == .authorized
        if isAuthorized {
            await fetchPlaylists()
        }
    }
    
    func fetchPlaylists() async {
        guard isAuthorized else { return }
        do {
            var request = MusicLibraryRequest<Playlist>()
            request.sort(by: \.name, ascending: true)
            let response = try await request.response()
            self.playlists = response.items
        } catch {
            print("Error fetching playlists: \(error)")
        }
    }
    
    func playSelectedPlaylistIfHeadphonesConnected() {
        guard isMusicEnabled, isAutoPlayEnabled, let playlistId = selectedPlaylistId else { return }
        
        if isHeadphonesConnected() {
            playPlaylist(id: playlistId)
        }
    }
    
    func resetSessionPlaybackState() {
        hasStartedSessionPlayback = false
        currentPlaylistTrackIds.removeAll()
    }
    
    func playPlaylist(id: String) {
        print("Attempting to play playlist \(id)")
        Task {
            do {
                if playlists.isEmpty {
                    await fetchPlaylists()
                }
                
                var targetPlaylist: Playlist?
                
                if let playlist = playlists.first(where: { $0.id.rawValue == id }) {
                    targetPlaylist = playlist
                } else {
                    var request = MusicLibraryRequest<Playlist>()
                    request.filter(matching: \.id, equalTo: MusicItemID(id))
                    let response = try await request.response()
                    targetPlaylist = response.items.first
                }
                
                if let playlist = targetPlaylist {
                    let detailedPlaylist = try await playlist.with([.tracks])
                    if let tracks = detailedPlaylist.tracks {
                        self.currentPlaylistTrackIds = Set(tracks.map { $0.id })
                    }
                    
                    SystemMusicPlayer.shared.queue = [playlist]
                    try await SystemMusicPlayer.shared.play()
                    hasStartedSessionPlayback = true
                }
            } catch {
                print("Error playing playlist: \(error)")
            }
        }
    }
    
    func stopPlaybackIfEnabled() {
        guard isMusicEnabled, isAutoStopEnabled else { return }
        SystemMusicPlayer.shared.stop()
    }
    
    func togglePlayback() {
        Task {
            if isPlaying {
                SystemMusicPlayer.shared.pause()
            } else {
                if let playlistId = selectedPlaylistId, !hasStartedSessionPlayback {
                    playPlaylist(id: playlistId)
                } else {
                    try? await SystemMusicPlayer.shared.play()
                }
            }
        }
    }
    
    private func isHeadphonesConnected() -> Bool {
        let route = AVAudioSession.sharedInstance().currentRoute
        return route.outputs.contains { port in
            port.portType == .headphones ||
            port.portType == .bluetoothA2DP ||
            port.portType == .bluetoothLE ||
            port.portType == .bluetoothHFP ||
            port.portType == .usbAudio ||
            port.portType == .airPlay
        }
    }
}
