import Foundation

/// Compares charger output, cable rating, and currently negotiated PDO to
/// identify the bottleneck — the "why is my Mac charging slowly?" answer.
public struct ChargingDiagnostic {
    public enum Bottleneck: Hashable {
        case noCharger
        case chargerLimit(chargerW: Int)
        case cableLimit(cableW: Int, chargerW: Int)
        case macLimit(negotiatedW: Int, chargerW: Int, cableW: Int?)
        case fine(negotiatedW: Int)
    }

    public let bottleneck: Bottleneck
    public let summary: String
    public let detail: String
    /// Charger ceiling in watts, available for display regardless of bottleneck.
    public let chargerW: Int?
    /// Cable rating in watts from the e-marker, available for display regardless of bottleneck.
    public let cableW: Int?

    public var isWarning: Bool {
        switch bottleneck {
        case .fine: return false
        default: return true
        }
    }
}

extension ChargingDiagnostic {
    public init?(
        port: AppleHPMInterface,
        sources: [PowerSource],
        identities: [USBPDSOP],
        adapter: AdapterInfo? = nil,
        wattageSource: ChargerWattageSource = .unknown,
        batteryFullyCharged: Bool? = nil
    ) {
        guard let source = PowerSource.preferredChargingSource(in: sources) else {
            return nil
        }
        // MagSafe (and at least some USB-C ports) keep the last negotiated
        // PDO around as cached data even after the charger is unplugged, so
        // a port that is actually idle still looks like it is drawing ~94W.
        // Gate on the port-level ConnectionActive flag instead of trusting
        // the PowerSource node alone.
        guard port.connectionActive == true else { return nil }

        // Adapter wattage resolution moved to ChargerWattageSource.resolve;
        // parameter kept for API compatibility.
        _ = adapter

        let chargerMaxW: Int
        let isAdapterFallback: Bool
        switch wattageSource {
        case .portNegotiated(let w):
            chargerMaxW = w
            isAdapterFallback = false
        case .systemAdapterFallback(let w):
            chargerMaxW = w
            isAdapterFallback = true
        case .unknown:
            chargerMaxW = Int((Double(source.maxPowerMW) / 1000).rounded())
            isAdapterFallback = false
        }

        let negotiatedW = source.winning.map { Int((Double($0.maxPowerMW) / 1000).rounded()) }

        if chargerMaxW <= 0 && (negotiatedW ?? 0) <= 0 {
            return nil
        }

        let cableMaxW: Int? = identities
            .first(where: { $0.endpoint == .sopPrime || $0.endpoint == .sopDoublePrime })?
            .cableVDO?.maxWatts

        self.chargerW = chargerMaxW > 0 ? chargerMaxW : nil
        self.cableW = cableMaxW

        // Order of suspicion:
        // 1. If cable rated below charger, cable is the bottleneck.
        // 2. If negotiated below both, the Mac (or current state) limits.
        // 3. Otherwise charger is the ceiling.
        if let cableW = cableMaxW, cableW < chargerMaxW {
            self.bottleneck = .cableLimit(cableW: cableW, chargerW: chargerMaxW)
            self.summary = String(localized: "Cable is limiting charging speed", bundle: _coreLocalizedBundle)
            self.detail = String(localized: "Charger can deliver up to \(chargerMaxW)W, but this cable is only rated to carry \(cableW)W. Replace the cable to charge faster.", bundle: _coreLocalizedBundle)
        } else if let n = negotiatedW, n < chargerMaxW - max(5, chargerMaxW / 10),
                  (cableMaxW.map { n < $0 - max(5, $0 / 10) } ?? true) {
            self.bottleneck = .macLimit(negotiatedW: n, chargerW: chargerMaxW, cableW: cableMaxW)
            self.summary = String(localized: "Charging at \(n)W (charger can do up to \(chargerMaxW)W)", bundle: _coreLocalizedBundle)
            self.detail = String(localized: "Both the charger and cable can do more, but the Mac is currently asking for less. This is normal once the battery is mostly full, or when the system is idle.", bundle: _coreLocalizedBundle)
        } else if let n = negotiatedW {
            self.bottleneck = .fine(negotiatedW: n)
            if batteryFullyCharged == true {
                // The battery-full state is shown here (the banner), not
                // in the PortSummary subtitle, so the two don't repeat.
                self.summary = String(localized: "Battery full, not charging", bundle: _coreLocalizedBundle)
                self.detail = String(localized: "Charger and cable are fine. The Mac will draw up to \(n)W when it needs to.", bundle: _coreLocalizedBundle)
            } else {
                self.summary = String(localized: "Charging well at \(n)W", bundle: _coreLocalizedBundle)
                self.detail = String(localized: "Charger and cable are well-matched.", bundle: _coreLocalizedBundle)
            }
        } else if isAdapterFallback {
            self.bottleneck = .chargerLimit(chargerW: chargerMaxW)
            self.summary = String(localized: "System reports charger at \(chargerMaxW)W", bundle: _coreLocalizedBundle)
            self.detail = String(localized: "Per-port negotiation data is not available. Wattage is from the system-wide adapter reading.", bundle: _coreLocalizedBundle)
        } else {
            self.bottleneck = .chargerLimit(chargerW: chargerMaxW)
            self.summary = String(localized: "Charger advertises up to \(chargerMaxW)W", bundle: _coreLocalizedBundle)
            self.detail = String(localized: "Negotiation hasn't completed yet.", bundle: _coreLocalizedBundle)
        }
    }
}
