import Foundation
import OSLog

private let logger = Logger(subsystem: AppIdentifiers.subsystem, category: "ServerStore")

/// Manages the list of paired servers.
///
/// Pure data store — no networking, no health checks.
/// Persists via `KeychainService` (tokens) + UserDefaults (order/index).
@MainActor @Observable
final class ServerStore {
    private(set) var servers: [PairedServer] = []

    init() {
        load()
    }

    // MARK: - CRUD

    /// Add a new paired server. If a server with the same fingerprint exists,
    /// updates its credentials instead (re-pair scenario).
    func addOrUpdate(_ server: PairedServer) {
        if let idx = servers.firstIndex(where: { $0.id == server.id }) {
            // Re-pair: update credentials, preserve local metadata
            var existing = servers[idx]
            existing.updateCredentials(from: server.credentials)
            servers[idx] = existing
            save(existing)
            logger.warning("Updated server \(server.name, privacy: .public) (re-pair)")
        } else {
            var newServer = server
            newServer.sortOrder = servers.count
            servers.append(newServer)
            save(newServer)
            logger.warning("Added server \(server.name, privacy: .public)")
        }
        saveIndex()
    }

    // periphery:ignore - used by ServerStoreTests via @testable import
    /// Add or update from validated `ServerCredentials`.
    /// Returns the `PairedServer` (new or updated), or `nil` if credentials lack a fingerprint.
    @discardableResult
    func addOrUpdate(from credentials: ServerCredentials) -> PairedServer? {
        guard let server = PairedServer(from: credentials, sortOrder: servers.count) else {
            logger.error("Cannot add server: credentials missing fingerprint")
            return nil
        }
        addOrUpdate(server)
        return self.server(for: server.id)
    }

    /// Remove a paired server by ID.
    func remove(id: String) {
        servers.removeAll { $0.id == id }
        KeychainService.deleteServer(id: id)
        saveIndex()
        logger.warning("Removed server \(id.prefix(16), privacy: .public)")
    }

    /// Update the badge icon for a server.
    func setBadgeIcon(id: String, to icon: ServerBadgeIcon) {
        guard let idx = servers.firstIndex(where: { $0.id == id }) else { return }
        servers[idx].badgeIcon = icon
        save(servers[idx])
    }

    /// Persist the client-only route restriction for one paired server.
    func setRouteMode(id: String, to mode: PairedServerRouteMode) {
        guard let idx = servers.firstIndex(where: { $0.id == id }) else { return }
        servers[idx].routeMode = mode
        save(servers[idx])
    }

    /// Look up a server by fingerprint ID.
    func server(for id: String) -> PairedServer? {
        servers.first { $0.id == id }
    }

    // periphery:ignore - used by ServerStoreTests via @testable import
    /// Look up which server owns a given host:port combination.
    func server(forHost host: String, port: Int) -> PairedServer? {
        servers.first { $0.host == host && $0.port == port }
    }

    // MARK: - Persistence

    private func load() {
        servers = KeychainService.loadServers()
        servers.sort { $0.sortOrder < $1.sortOrder }
    }

    private func save(_ server: PairedServer) {
        do {
            try KeychainService.saveServer(server)
        } catch {
            logger.error("Failed to save server \(server.name, privacy: .public): \(error.localizedDescription, privacy: .public)")
        }
    }

    /// Persist the ordered list of server IDs to both shared and standard UserDefaults.
    ///
    /// Shared suite: readable by widget extension for Live Activity intents.
    /// Standard: backward-compatible fallback.
    private func saveIndex() {
        let ids = servers.map(\.id)
        SharedConstants.sharedDefaults.set(ids, forKey: SharedConstants.pairedServerIdsKey)
        UserDefaults.standard.set(ids, forKey: SharedConstants.pairedServerIdsKey)
    }
}
