import AppIntents
import Foundation

enum StateIntentError: Error {
    case StateIntentUnknownError
    case NoBG
    case NoIOBCOB
}

@available(iOS 16, *) struct StateiAPSResults: AppEntity {
    static var defaultQuery = StateBGQuery()

    static var typeDisplayRepresentation: TypeDisplayRepresentation = "iAPS State Result"

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(glucose)")
    }

    var id: UUID
    @Property(title: "Glucose") var glucose: String

    @Property(title: "Trend") var trend: String

    @Property(title: "Delta") var delta: String

    @Property(title: "Date") var date: Date

    @Property(title: "IOB") var iob: Double?

    @Property(title: "COB") var cob: Double?

    @Property(title: "unit") var unit: String?

    init(glucose: String, trend: String, delta: String, date: Date, iob: Double, cob: Double, unit: GlucoseUnits) {
        id = UUID()
        self.glucose = glucose
        self.trend = trend
        self.delta = delta
        self.date = date
        self.iob = iob
        self.cob = cob
        self.unit = unit.rawValue
    }
}

@available(iOS 16.0, *) struct StateBGQuery: EntityQuery {
    func entities(for _: [StateiAPSResults.ID]) async throws -> [StateiAPSResults] {
        []
    }

    func suggestedEntities() async throws -> [StateiAPSResults] {
        []
    }
}

@available(iOS 16.0, *) final class StateIntentRequest: BaseIntentsRequest {
    func getLastBG() throws -> (dateGlucose: Date, glucose: String, trend: String, delta: String) {
        let glucose = glucoseStorage.recent()
        guard let lastGlucose = glucose.last, let glucoseValue = lastGlucose.glucose else { throw StateIntentError.NoBG }
        let delta = glucose.count >= 2 ? glucoseValue - (glucose[glucose.count - 2].glucose ?? 0) : nil
        let units = settingsManager.settings.units

        let glucoseText = glucoseFormatter
            .string(from: Double(
                units == .mmolL ? glucoseValue
                    .asMmolL : Decimal(glucoseValue)
            ) as NSNumber)!
        let directionText = lastGlucose.direction?.rawValue ?? "none"
        let deltaText = delta
            .map {
                self.deltaFormatter
                    .string(from: Double(
                        units == .mmolL ? $0
                            .asMmolL : Decimal($0)
                    ) as NSNumber)!
            } ?? "--"

        return (lastGlucose.dateString, glucoseText, directionText, deltaText)
    }

    func getIOB_COB() throws -> (iob: Double, cob: Double) {
        let iob = suggestion?.iob ?? 0.0
        let cob = suggestion?.cob ?? 0.0
        let iob_double = Double(truncating: iob as NSNumber)
        let cob_double = Double(truncating: cob as NSNumber)
        return (iob_double, cob_double)
    }

    private var suggestion: Suggestion? {
        fileStorage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
    }

    private var glucoseFormatter: NumberFormatter {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.maximumFractionDigits = 0
        if settingsManager.settings.units == .mmolL {
            formatter.minimumFractionDigits = 1
            formatter.maximumFractionDigits = 1
        }
        formatter.roundingMode = .halfUp
        return formatter
    }

    private var deltaFormatter: NumberFormatter {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.maximumFractionDigits = 1
        formatter.positivePrefix = "+"
        return formatter
    }
}
