import Foundation
import WatchConnectivity

/// WatchState manages the communication between the Watch app and the iPhone app using WatchConnectivity.
/// It handles glucose data synchronization and sending treatment requests (bolus, carbs) to the phone.
@Observable final class WatchState: NSObject, WCSessionDelegate {
    // MARK: - Properties

    /// The WatchConnectivity session instance used for communication
    private var session: WCSession?
    /// Indicates if the paired iPhone is currently reachable
    var isReachable = false

    var currentGlucose: String = "--"
    var trend: String? = ""
    var delta: String? = "--"
    var glucoseValues: [(date: Date, glucose: Double)] = []
    var cob: String? = "--"
    var iob: String? = "--"
    var lastLoopTime: String? = "--"
    var overridePresets: [OverridePresetWatch] = []
    var tempTargetPresets: [TempTargetPresetWatch] = []

    /// treatments inputs
    /// used to store carbs for combined meal-bolus-treatments
    var carbsAmount: Int = 0
    var fatAmount: Int = 0
    var proteinAmount: Int = 0
    var bolusAmount = 0.0
    var activeBolusAmount = 0.0
    var confirmationProgress = 0.0

    var bolusProgress: Double = 0.0
    var isBolusCanceled = false

    // Safety limits
    var maxBolus: Decimal = 10
    var maxCarbs: Decimal = 250
    var maxFat: Decimal = 250
    var maxProtein: Decimal = 250
    var maxIOB: Decimal = 0
    var maxCOB: Decimal = 120

    // acknowlegement handling
    var showCommsAnimation: Bool = false
    var showAcknowledgmentBanner: Bool = false
    var acknowledgementStatus: AcknowledgementStatus = .pending
    var acknowledgmentMessage: String = ""
    // Meal bolus-specific properties
    var mealBolusStep: MealBolusStep = .savingCarbs
    var isMealBolusCombo: Bool = false

    var showBolusProgressOverlay: Bool {
        (!showAcknowledgmentBanner || !showCommsAnimation || !showCommsAnimation) && bolusProgress > 0 && bolusProgress < 1.0 &&
            !isBolusCanceled
    }

    override init() {
        super.init()
        setupSession()
    }

    /// Configures the WatchConnectivity session if supported on the device
    private func setupSession() {
        if WCSession.isSupported() {
            let session = WCSession.default
            session.delegate = self
            session.activate()
            self.session = session
        } else {
            print("⌚️ WCSession is not supported on this device")
        }
    }

    // MARK: - Send Data to Phone

    /// Sends a bolus insulin request to the paired iPhone
    /// - Parameters:
    ///   - amount: The insulin amount to be delivered
    func sendBolusRequest(_ amount: Decimal) {
        guard let session = session, session.isReachable else { return }
        isBolusCanceled = false // Reset canceled state when starting new bolus
        activeBolusAmount = Double(truncating: amount as NSNumber) // Set active bolus amount

        let message: [String: Any] = [
            "bolus": amount
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("Error sending bolus request: \(error.localizedDescription)")
        }

        // Display pending communication animation
        showCommsAnimation = true
    }

    /// Sends a carbohydrate entry request to the paired iPhone
    /// - Parameters:
    ///   - amount: The amount of carbs in grams
    ///   - date: The timestamp for the carb entry (defaults to current time)
    func sendCarbsRequest(_ amount: Int, _ date: Date = Date()) {
        guard let session = session, session.isReachable else { return }

        let message: [String: Any] = [
            "carbs": amount,
            "date": date.timeIntervalSince1970
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("Error sending carbs request: \(error.localizedDescription)")
        }

        // Display pending communication animation
        showCommsAnimation = true
    }

    /// Sends a meal and bolus insulin combo request to the paired iPhone
    /// - Parameters:
    ///   - amount: The insulin amount to be delivered
    ///   - isExternal: Indicates if the bolus is from an external source
    func sendMealBolusComboRequest(carbsAmount _: Decimal, bolusAmount: Decimal, _ date: Date = Date()) {
        guard let session = session, session.isReachable else { return }

        let message: [String: Any] = [
            "bolus": bolusAmount,
            "carbs": bolusAmount,
            "date": date.timeIntervalSince1970
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("Error sending meal bolus combo request: \(error.localizedDescription)")
        }

        // Display pending communication animation
        showCommsAnimation = true
        isMealBolusCombo = true
    }

    func sendCancelOverrideRequest() {
        guard let session = session, session.isReachable else { return }

        let message: [String: Any] = [
            "cancelOverride": true
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("⌚️ Error sending cancel override request: \(error.localizedDescription)")
        }
    }

    func sendActivateOverrideRequest(presetName: String) {
        guard let session = session, session.isReachable else { return }

        let message: [String: Any] = [
            "activateOverride": presetName
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("⌚️ Error sending activate override request: \(error.localizedDescription)")
        }
    }

    func sendCancelTempTargetRequest() {
        guard let session = session, session.isReachable else { return }

        let message: [String: Any] = [
            "cancelTempTarget": true
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("⌚️ Error sending cancel temp target request: \(error.localizedDescription)")
        }
    }

    func sendActivateTempTargetRequest(presetName: String) {
        guard let session = session, session.isReachable else { return }

        let message: [String: Any] = [
            "activateTempTarget": presetName
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("⌚️ Error sending activate temp target request: \(error.localizedDescription)")
        }
    }

    func sendCancelBolusRequest() {
        isBolusCanceled = true

        guard let session = session, session.isReachable else { return }

        let message: [String: Any] = [
            "cancelBolus": true
        ]

        session.sendMessage(message, replyHandler: nil) { error in
            print("Error sending cancel bolus request: \(error.localizedDescription)")
        }

        // Reset when cancelled
        bolusProgress = 0
        activeBolusAmount = 0
    }

    // MARK: – Handle Acknowledgement Messages FROM Phone

    private func handleAcknowledgment(success: Bool, message: String, isFinal: Bool = true) {
        if success {
            print("⌚️ Acknowledgment received: \(message)")
            showCommsAnimation = false // Hide progress animation
            acknowledgementStatus = .success
            acknowledgmentMessage = "\(message)"
        } else {
            print("⌚️ Acknowledgment failed: \(message)")
            showCommsAnimation = false // Hide progress animation
            acknowledgementStatus = .failure
            acknowledgmentMessage = "\(message)"
        }

        if isFinal {
            showAcknowledgmentBanner = true
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.showAcknowledgmentBanner = false
            }
        }
    }

    // MARK: - WCSessionDelegate

    /// Called when the session has completed activation
    /// Updates the reachability status and logs the activation state
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        DispatchQueue.main.async {
            if let error = error {
                print("⌚️ Watch session activation failed: \(error.localizedDescription)")
                return
            }

            print("⌚️ Watch session activated with state: \(activationState.rawValue)")
            self.isReachable = session.isReachable
            print("⌚️ Watch isReachable after activation: \(session.isReachable)")
        }
    }

    /// Handles incoming messages from the paired iPhone
    /// Updates local glucose data, trend, and delta information
    func session(_: WCSession, didReceiveMessage message: [String: Any]) {
        print("⌚️ Watch received message: \(message)")

        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }

            if let acknowledged = message["acknowledged"] as? Bool,
               let ackMessage = message["message"] as? String
            {
                switch ackMessage {
                case "Saving carbs...":
                    self.isMealBolusCombo = true
                    self.mealBolusStep = .savingCarbs
                    self.showCommsAnimation = true
                    self.handleAcknowledgment(success: acknowledged, message: ackMessage, isFinal: false)
                case "Enacting bolus...":
                    self.isMealBolusCombo = true
                    self.mealBolusStep = .enactingBolus
                    self.showCommsAnimation = true
                    self.handleAcknowledgment(success: acknowledged, message: ackMessage, isFinal: false)
                case "Carbs and bolus logged successfully":
                    self.isMealBolusCombo = false
                    self.handleAcknowledgment(success: acknowledged, message: ackMessage, isFinal: true)
                default:
                    self.isMealBolusCombo = false
                    self.handleAcknowledgment(success: acknowledged, message: ackMessage, isFinal: true)
                }
            }

            if let currentGlucose = message["currentGlucose"] as? String {
                self.currentGlucose = currentGlucose
            }

            if let trend = message["trend"] as? String {
                self.trend = trend
            }

            if let delta = message["delta"] as? String {
                self.delta = delta
            }

            if let iob = message["iob"] as? String {
                self.iob = iob
            }

            if let cob = message["cob"] as? String {
                self.cob = cob
            }

            if let lastLoopTime = message["lastLoopTime"] as? String {
                self.lastLoopTime = lastLoopTime
            }

            if let glucoseData = message["glucoseValues"] as? [[String: Any]] {
                self.glucoseValues = glucoseData.compactMap { data in
                    guard let glucose = data["glucose"] as? Double,
                          let timestamp = data["date"] as? TimeInterval
                    else { return nil }

                    return (Date(timeIntervalSince1970: timestamp), glucose)
                }
                .sorted { $0.date < $1.date }
            }

            if let overrideData = message["overridePresets"] as? [[String: Any]] {
                self.overridePresets = overrideData.compactMap { data in
                    guard let name = data["name"] as? String,
                          let isEnabled = data["isEnabled"] as? Bool
                    else { return nil }
                    return OverridePresetWatch(name: name, isEnabled: isEnabled)
                }
            }

            if let tempTargetData = message["tempTargetPresets"] as? [[String: Any]] {
                self.tempTargetPresets = tempTargetData.compactMap { data in
                    guard let name = data["name"] as? String,
                          let isEnabled = data["isEnabled"] as? Bool
                    else { return nil }
                    return TempTargetPresetWatch(name: name, isEnabled: isEnabled)
                }
            }

            if let bolusProgress = message["bolusProgress"] as? Double {
                if !self.isBolusCanceled {
                    self.bolusProgress = bolusProgress
                }
            }

            if let bolusWasCanceled = message["bolusCanceled"] as? Bool, bolusWasCanceled {
                self.bolusProgress = 0
                self.activeBolusAmount = 0
                return
            }

            // Debug print für die Safety Limits
            if let maxBolusValue = message["maxBolus"] {
                print("⌚️ Received maxBolus: \(maxBolusValue) of type \(type(of: maxBolusValue))")
                if let decimalValue = (maxBolusValue as? NSNumber)?.decimalValue {
                    self.maxBolus = decimalValue
                    print("⌚️ Converted maxBolus to: \(decimalValue)")
                }
            }

            if let maxCarbsValue = message["maxCarbs"] {
                if let decimalValue = (maxCarbsValue as? NSNumber)?.decimalValue {
                    self.maxCarbs = decimalValue
                }
            }

            if let maxFatValue = message["maxFat"] {
                if let decimalValue = (maxFatValue as? NSNumber)?.decimalValue {
                    self.maxFat = decimalValue
                }
            }

            if let maxProteinValue = message["maxProtein"] {
                if let decimalValue = (maxProteinValue as? NSNumber)?.decimalValue {
                    self.maxProtein = decimalValue
                }
            }

            if let maxIOBValue = message["maxIOB"] {
                if let decimalValue = (maxIOBValue as? NSNumber)?.decimalValue {
                    self.maxIOB = decimalValue
                }
            }

            if let maxCOBValue = message["maxCOB"] {
                if let decimalValue = (maxCOBValue as? NSNumber)?.decimalValue {
                    self.maxCOB = decimalValue
                }
            }
        }
    }

    /// Called when the reachability status of the paired iPhone changes
    /// Updates the local reachability status
    func sessionReachabilityDidChange(_ session: WCSession) {
        DispatchQueue.main.async {
            self.isReachable = session.isReachable
            print("⌚️ Watch reachability changed: \(session.isReachable)")
        }
    }
}
