// LoopFollow
// RemoteCommandSettings.swift

import Foundation
import HealthKit

struct RemoteCommandSettings: Codable {
    let remoteType: RemoteType
    let user: String
    let sharedSecret: String
    let remoteApnsKey: String
    let remoteKeyId: String
    let teamId: String?
    let maxBolus: Double
    let maxCarbs: Double
    let maxProtein: Double
    let maxFat: Double
    let mealWithBolus: Bool
    let mealWithFatProtein: Bool
    let productionEnvironment: Bool
    let loopAPNSQrCodeURL: String
    let url: String
    let token: String
    let version: String

    init(
        remoteType: RemoteType,
        user: String,
        sharedSecret: String,
        remoteApnsKey: String,
        remoteKeyId: String,
        teamId: String?,
        maxBolus: Double,
        maxCarbs: Double,
        maxProtein: Double,
        maxFat: Double,
        mealWithBolus: Bool,
        mealWithFatProtein: Bool,
        productionEnvironment: Bool,
        loopAPNSQrCodeURL: String,
        url: String,
        token: String
    ) {
        self.remoteType = remoteType
        self.user = user
        self.sharedSecret = sharedSecret
        self.remoteApnsKey = remoteApnsKey
        self.remoteKeyId = remoteKeyId
        self.teamId = teamId
        self.maxBolus = maxBolus
        self.maxCarbs = maxCarbs
        self.maxProtein = maxProtein
        self.maxFat = maxFat
        self.mealWithBolus = mealWithBolus
        self.mealWithFatProtein = mealWithFatProtein
        self.productionEnvironment = productionEnvironment
        self.loopAPNSQrCodeURL = loopAPNSQrCodeURL
        self.url = url
        self.token = token
        version = "1.0"
    }

    /// Creates RemoteCommandSettings from the current Storage values
    static func fromCurrentStorage() -> RemoteCommandSettings {
        let storage = Storage.shared

        return RemoteCommandSettings(
            remoteType: storage.remoteType.value,
            user: storage.user.value,
            sharedSecret: storage.sharedSecret.value,
            remoteApnsKey: storage.remoteApnsKey.value,
            remoteKeyId: storage.remoteKeyId.value,
            teamId: storage.teamId.value,
            maxBolus: storage.maxBolus.value.doubleValue(for: .internationalUnit()),
            maxCarbs: storage.maxCarbs.value.doubleValue(for: .gram()),
            maxProtein: storage.maxProtein.value.doubleValue(for: .gram()),
            maxFat: storage.maxFat.value.doubleValue(for: .gram()),
            mealWithBolus: storage.mealWithBolus.value,
            mealWithFatProtein: storage.mealWithFatProtein.value,
            productionEnvironment: storage.productionEnvironment.value,
            loopAPNSQrCodeURL: storage.loopAPNSQrCodeURL.value,
            url: storage.url.value,
            token: storage.token.value
        )
    }

    /// Applies the settings to the current Storage
    func applyToStorage() {
        let storage = Storage.shared

        storage.remoteType.value = remoteType
        storage.user.value = user
        storage.sharedSecret.value = sharedSecret
        storage.remoteApnsKey.value = remoteApnsKey
        storage.remoteKeyId.value = remoteKeyId
        storage.teamId.value = teamId
        storage.maxBolus.value = HKQuantity(unit: .internationalUnit(), doubleValue: maxBolus)
        storage.maxCarbs.value = HKQuantity(unit: .gram(), doubleValue: maxCarbs)
        storage.maxProtein.value = HKQuantity(unit: .gram(), doubleValue: maxProtein)
        storage.maxFat.value = HKQuantity(unit: .gram(), doubleValue: maxFat)
        storage.mealWithBolus.value = mealWithBolus
        storage.mealWithFatProtein.value = mealWithFatProtein
        storage.productionEnvironment.value = productionEnvironment
        storage.loopAPNSQrCodeURL.value = loopAPNSQrCodeURL
        storage.url.value = url
        storage.token.value = token

        // Automatically set device type based on remote type
        switch remoteType {
        case .loopAPNS:
            storage.device.value = "Loop"
        case .trc:
            storage.device.value = "Trio"
        case .nightscout:
            // For Nightscout, we don't automatically set device type
            // as it should be determined by the actual connection
            break
        case .none:
            // For none, we don't change the device type
            break
        }
    }

    /// Encodes the settings to a JSON string for QR code generation
    func encodeToJSON() -> String? {
        do {
            let data = try JSONEncoder().encode(self)
            return String(data: data, encoding: .utf8)
        } catch {
            return nil
        }
    }

    /// Decodes settings from a JSON string
    static func decodeFromJSON(_ jsonString: String) -> RemoteCommandSettings? {
        guard let data = jsonString.data(using: .utf8) else {
            return nil
        }

        do {
            return try JSONDecoder().decode(RemoteCommandSettings.self, from: data)
        } catch {
            return nil
        }
    }

    /// Checks if the settings are valid for the given remote type
    func hasValidSettings() -> Bool {
        switch remoteType {
        case .none:
            return true
        case .nightscout:
            return !user.isEmpty
        case .trc:
            return !user.isEmpty && !sharedSecret.isEmpty && !remoteApnsKey.isEmpty && !remoteKeyId.isEmpty
        case .loopAPNS:
            return !remoteKeyId.isEmpty && !remoteApnsKey.isEmpty && teamId != nil && !loopAPNSQrCodeURL.isEmpty
        }
    }

    /// Validates URL and token compatibility with current storage
    /// Returns a tuple with (isCompatible, shouldPromptForURL, shouldPromptForToken, message)
    func validateCompatibilityWithCurrentStorage() -> (isCompatible: Bool, shouldPromptForURL: Bool, shouldPromptForToken: Bool, message: String) {
        let storage = Storage.shared
        let currentURL = storage.url.value
        let currentToken = storage.token.value

        var shouldPromptForURL = false
        var shouldPromptForToken = false
        var message = ""

        // Check if current user has URL set
        let hasCurrentURL = !currentURL.isEmpty
        let hasCurrentToken = !currentToken.isEmpty

        // Check if scanned settings have URL/token
        let hasScannedURL = !url.isEmpty
        let hasScannedToken = !token.isEmpty

        // If current user doesn't have URL but scanned settings do, prompt to set it
        if !hasCurrentURL, hasScannedURL {
            shouldPromptForURL = true
            message = "The scanned settings include a Nightscout URL. Would you like to set this as your Nightscout address?"
        }

        // If current user doesn't have token but scanned settings do, prompt to set it
        if !hasCurrentToken, hasScannedToken {
            shouldPromptForToken = true
            if !message.isEmpty {
                message += "\n\nThe scanned settings also include a token. Would you like to set this as your access token?"
            } else {
                message = "The scanned settings include a token. Would you like to set this as your access token?"
            }
        }

        // If both have URLs but they don't match, show warning
        if hasCurrentURL, hasScannedURL, currentURL != url {
            shouldPromptForURL = true
            message = "The scanned Nightscout URL (\(url)) doesn't match your current Nightscout address (\(currentURL)). Would you like to change your Nightscout address to match the scanned settings?"
        }

        // If both have tokens but they don't match, show warning
        if hasCurrentToken, hasScannedToken, currentToken != token {
            shouldPromptForToken = true
            if !message.isEmpty {
                message += "\n\nThe scanned token doesn't match your current access token. Would you like to update your token?"
            } else {
                message = "The scanned token doesn't match your current access token. Would you like to update your token?"
            }
        }

        let isCompatible = !shouldPromptForURL && !shouldPromptForToken

        return (isCompatible, shouldPromptForURL, shouldPromptForToken, message)
    }
}
