import Foundation
import Observation

@Observable
@MainActor
public final class StationLibrary {
    var selectedPage: StationPage = .top
    var stations: [RadioStation] = []
    var favorites: [RadioStation] = []
    var manualStations: [RadioStation] = []
    var recentStations: [RadioStation] = []
    var groups: [StationGroup] = []
    var selectedGroupID: String?
    var selectedStationID: RadioStation.ID?
    var filterQuery: String = ""
    var sortMode: StationSortMode = .name
    var isLoading = false
    var lastError: String?
    var lastStationName = ""
    var lastStationURL = ""
    var volume: Double = 0.55

    private var client: RadioBrowserClient
    private let persistence: PersistenceStore

    init(client: RadioBrowserClient, persistence: PersistenceStore) {
        self.client = client
        self.persistence = persistence
        persistence.bootstrapImportIfNeeded()
        favorites = persistence.loadFavorites().map { station in
            var copy = station
            copy.isFavorite = true
            return copy
        }
        manualStations = persistence.loadManualStations()
        groups = persistence.loadGroups().sorted { $0.sortOrder < $1.sortOrder }
        let state = persistence.loadPlaybackState()
        lastStationName = state.lastStationName
        lastStationURL = state.lastStationURL
        volume = min(max(state.volume, 0), 1)
        recentStations = Array(state.recentStations.prefix(20))
    }

    var visibleStations: [RadioStation] {
        let source: [RadioStation]
        switch selectedPage {
        case .favorites:
            source = favorites
        case .recent:
            source = recentStations
        case .manual:
            source = manualStations
        case .group:
            source = stationsForSelectedGroup()
        case .top, .germany, .netherlands, .world, .search:
            source = stations
        }

        let filtered: [RadioStation]
        if filterQuery.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            filtered = source
        } else {
            let query = filterQuery.lowercased()
            filtered = source.filter { $0.searchableText.contains(query) }
        }

        return sorted(filtered)
    }

    var selectedStation: RadioStation? {
        guard let selectedStationID else { return visibleStations.first }
        return visibleStations.first { $0.id == selectedStationID }
    }

    var canResume: Bool {
        !lastStationURL.isEmpty
    }

    func updateBaseURL(_ url: URL) {
        client.baseURL = url
    }

    func loadSelectedPage() async {
        await load(selectedPage)
    }

    func load(_ page: StationPage) async {
        selectedPage = page
        lastError = nil

        switch page {
        case .favorites, .recent, .manual, .search, .group:
            selectedStationID = visibleStations.first?.id
            return
        default:
            break
        }

        isLoading = true
        defer { isLoading = false }

        do {
            let loaded: [RadioStation]
            switch page {
            case .top:
                loaded = try await client.topStations()
            case .germany:
                loaded = try await client.germanStations()
            case .netherlands:
                loaded = try await client.dutchStations()
            case .world:
                loaded = try await client.worldStations()
            case .search, .favorites, .recent, .manual, .group:
                loaded = []
            }
            stations = markFavorites(loaded)
            selectedStationID = visibleStations.first?.id
        } catch {
            lastError = error.localizedDescription
        }
    }

    func search(_ query: String) async {
        let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines)
        guard trimmed.count >= 2 else { return }
        selectedPage = .search
        filterQuery = ""
        isLoading = true
        defer { isLoading = false }
        do {
            stations = markFavorites(try await client.search(trimmed))
            selectedStationID = visibleStations.first?.id
            lastError = nil
        } catch {
            lastError = error.localizedDescription
        }
    }

    func loadTag(_ tag: String) async {
        isLoading = true
        defer { isLoading = false }
        do {
            stations = markFavorites(try await client.stations(tag: tag))
            selectedPage = .world
            selectedStationID = visibleStations.first?.id
            lastError = nil
        } catch {
            lastError = error.localizedDescription
        }
    }

    func loadCountry(_ code: String) async {
        isLoading = true
        defer { isLoading = false }
        do {
            stations = markFavorites(try await client.stations(countryCode: code))
            selectedPage = .world
            selectedStationID = visibleStations.first?.id
            lastError = nil
        } catch {
            lastError = error.localizedDescription
        }
    }

    func select(_ station: RadioStation) {
        selectedStationID = station.id
    }

    func toggleFavorite(_ station: RadioStation) {
        if let index = favorites.firstIndex(where: { matches($0, station) }) {
            favorites.remove(at: index)
        } else {
            var copy = station
            copy.isFavorite = true
            favorites.append(copy)
        }
        favorites = sorted(favorites).map { station in
            var copy = station
            copy.isFavorite = true
            return copy
        }
        persistence.saveFavorites(favorites)
        stations = markFavorites(stations)
        manualStations = markFavorites(manualStations)
    }

    func isFavorite(_ station: RadioStation) -> Bool {
        favorites.contains { matches($0, station) }
    }

    func addManualStation(name: String, url: String, country: String) {
        let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)
        let cleanURL = url.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !cleanName.isEmpty, URL(string: cleanURL) != nil else { return }

        let station = RadioStation(
            uuid: UUID().uuidString,
            name: cleanName,
            url: cleanURL,
            resolvedURL: cleanURL,
            country: country.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? String(localized: "Manual") : country,
            codec: "unknown",
            isManual: true
        )
        manualStations.insert(station, at: 0)
        persistence.saveManualStations(manualStations)
        selectedPage = .manual
        selectedStationID = station.id
    }

    func recordPlayback(_ station: RadioStation) {
        lastStationName = station.name
        lastStationURL = station.playbackURLString
        var normalized = station
        normalized.isFavorite = isFavorite(station)
        recentStations.removeAll { matches($0, normalized) }
        recentStations.insert(normalized, at: 0)
        recentStations = Array(recentStations.prefix(20))
        savePlaybackState()
    }

    func saveVolume(_ volume: Double) {
        self.volume = min(max(volume, 0), 1)
        savePlaybackState()
    }

    func createGroup(name: String) {
        let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !cleanName.isEmpty else { return }
        let maxSort = groups.map { $0.sortOrder }.max() ?? 0
        let group = StationGroup(name: cleanName, sortOrder: maxSort + 1)
        groups.append(group)
        persistence.saveGroups(groups)
    }

    func deleteGroup(_ group: StationGroup) {
        groups.removeAll { $0.id == group.id }
        persistence.saveGroups(groups)
        if selectedGroupID == group.id {
            selectedGroupID = nil
            selectedPage = .top
        }
    }

    func renameGroup(_ group: StationGroup, to name: String) {
        let cleanName = name.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !cleanName.isEmpty else { return }
        if let index = groups.firstIndex(where: { $0.id == group.id }) {
            groups[index].name = cleanName
            persistence.saveGroups(groups)
        }
    }

    func addStationToGroup(_ station: RadioStation, group: StationGroup) {
        guard !group.stationIDs.contains(station.id) else { return }
        if let index = groups.firstIndex(where: { $0.id == group.id }) {
            groups[index].stationIDs.append(station.id)
            persistence.saveGroups(groups)
        }
    }

    func removeStationFromGroup(_ station: RadioStation, group: StationGroup) {
        if let index = groups.firstIndex(where: { $0.id == group.id }) {
            groups[index].stationIDs.removeAll { $0 == station.id }
            persistence.saveGroups(groups)
        }
    }

    func isStationInGroup(_ station: RadioStation, group: StationGroup) -> Bool {
        group.stationIDs.contains(station.id)
    }

    func selectGroup(_ group: StationGroup) {
        selectedPage = .group
        selectedGroupID = group.id
        filterQuery = ""
        selectedStationID = visibleStations.first?.id
    }

    private func stationsForSelectedGroup() -> [RadioStation] {
        guard let group = groups.first(where: { $0.id == selectedGroupID }) else { return [] }
        let allStations = stations + favorites + manualStations + recentStations
        var seen = Set<String>()
        return group.stationIDs.compactMap { id in
            guard seen.insert(id).inserted else { return nil }
            return allStations.first { $0.id == id }
        }.compactMap { $0 }
    }

    func resumeStation() -> RadioStation? {
        guard !lastStationURL.isEmpty else { return nil }
        if let known = (stations + favorites + manualStations + recentStations).first(where: { $0.playbackURLString == lastStationURL }) {
            return known
        }
        return RadioStation(name: lastStationName.isEmpty ? String(localized: "Last played") : lastStationName, url: lastStationURL, resolvedURL: lastStationURL)
    }

    private func markFavorites(_ source: [RadioStation]) -> [RadioStation] {
        source.map { station in
            var copy = station
            copy.isFavorite = isFavorite(station)
            return copy
        }
    }

    private func sorted(_ source: [RadioStation]) -> [RadioStation] {
        switch sortMode {
        case .name:
            source.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
        case .bitrate:
            source.sorted { $0.bitrate > $1.bitrate }
        case .votes:
            source.sorted { $0.votes > $1.votes }
        }
    }

    private func savePlaybackState() {
        persistence.savePlaybackState(
            PlaybackState(
                lastStationName: lastStationName,
                lastStationURL: lastStationURL,
                volume: volume,
                recentStations: recentStations
            )
        )
    }

    private func matches(_ lhs: RadioStation, _ rhs: RadioStation) -> Bool {
        if !lhs.uuid.isEmpty && !rhs.uuid.isEmpty {
            return lhs.uuid == rhs.uuid
        }
        return lhs.playbackURLString == rhs.playbackURLString
    }
}
