import Foundation

public struct PersistenceStore: Sendable {
    let applicationSupportDirectory: URL
    let cacheDirectory: URL
    let linuxConfigDirectory: URL

    public init(
        applicationSupportDirectory: URL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("BearWave"),
        cacheDirectory: URL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].appendingPathComponent("BearWave"),
        linuxConfigDirectory: URL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".config/bearwave")
    ) {
        self.applicationSupportDirectory = applicationSupportDirectory
        self.cacheDirectory = cacheDirectory
        self.linuxConfigDirectory = linuxConfigDirectory
    }

    var apiCacheDirectory: URL {
        cacheDirectory.appendingPathComponent("api_cache")
    }

    func bootstrapImportIfNeeded() {
        if !FileManager.default.fileExists(atPath: favoritesURL.path) {
            copyIfPresent(from: linuxConfigDirectory.appendingPathComponent("favorites.json"), to: favoritesURL)
        }
        if !FileManager.default.fileExists(atPath: stateURL.path) {
            copyIfPresent(from: linuxConfigDirectory.appendingPathComponent("state.json"), to: stateURL)
        }
    }

    func loadFavorites() -> [RadioStation] {
        load([RadioStation].self, from: favoritesURL) ?? []
    }

    func saveFavorites(_ stations: [RadioStation]) {
        save(stations, to: favoritesURL)
    }

    func loadManualStations() -> [RadioStation] {
        load([RadioStation].self, from: manualURL) ?? []
    }

    func saveManualStations(_ stations: [RadioStation]) {
        save(stations, to: manualURL)
    }

    func loadGroups() -> [StationGroup] {
        load([StationGroup].self, from: groupsURL) ?? []
    }

    func saveGroups(_ groups: [StationGroup]) {
        save(groups, to: groupsURL)
    }

    func loadPlaybackState() -> PlaybackState {
        load(PlaybackState.self, from: stateURL) ?? PlaybackState()
    }

    func savePlaybackState(_ state: PlaybackState) {
        save(state, to: stateURL)
    }

    private var favoritesURL: URL {
        applicationSupportDirectory.appendingPathComponent("favorites.json")
    }

    private var manualURL: URL {
        applicationSupportDirectory.appendingPathComponent("manual-stations.json")
    }

    private var stateURL: URL {
        applicationSupportDirectory.appendingPathComponent("state.json")
    }

    private var groupsURL: URL {
        applicationSupportDirectory.appendingPathComponent("groups.json")
    }

    private func load<T: Decodable>(_ type: T.Type, from url: URL) -> T? {
        guard let data = try? Data(contentsOf: url) else { return nil }
        return try? JSONDecoder().decode(type, from: data)
    }

    private func save<T: Encodable>(_ value: T, to url: URL) {
        do {
            try FileManager.default.createDirectory(at: applicationSupportDirectory, withIntermediateDirectories: true)
            let data = try JSONEncoder.prettyBearWave.encode(value)
            try data.write(to: url, options: .atomic)
        } catch {
            assertionFailure("Failed to save \(url.lastPathComponent): \(error)")
        }
    }

    private func copyIfPresent(from source: URL, to destination: URL) {
        guard FileManager.default.fileExists(atPath: source.path) else { return }
        do {
            try FileManager.default.createDirectory(at: destination.deletingLastPathComponent(), withIntermediateDirectories: true)
            try FileManager.default.copyItem(at: source, to: destination)
        } catch {
            assertionFailure("Failed to import \(source.lastPathComponent): \(error)")
        }
    }
}

struct PlaybackState: Codable, Equatable, Sendable {
    var lastStationName: String
    var lastStationURL: String
    var volume: Double
    var recentStations: [RadioStation]

    init(
        lastStationName: String = "",
        lastStationURL: String = "",
        volume: Double = 0.55,
        recentStations: [RadioStation] = []
    ) {
        self.lastStationName = lastStationName
        self.lastStationURL = lastStationURL
        self.volume = volume
        self.recentStations = recentStations
    }
}

extension JSONEncoder {
    static var prettyBearWave: JSONEncoder {
        let encoder = JSONEncoder()
        encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
        return encoder
    }
}
