import Foundation

struct TemperatureMeasurement: Identifiable {
    
    var sensor0: TemperatureValue
    
    var sensor1: TemperatureValue

    /// The seconds since 1970
    var id: Int

    var date: Date {
        get {
            Date(seconds: id)
        }
        set {
            id = newValue.seconds
        }
    }

    var secondsToNow: Int {
        id - Date().seconds
    }
    
    var maximumValue: Double? {
        guard let s0 = sensor0.optionalValue else {
            return sensor1.optionalValue
        }
        guard let s1 = sensor1.optionalValue else {
            return s0
        }
        return max(s0, s1)
    }
    
    var minimumValue: Double? {
        guard let s0 = sensor0.optionalValue else {
            return sensor1.optionalValue
        }
        guard let s1 = sensor1.optionalValue else {
            return s0
        }
        return min(s0, s1)
    }

    var averageValue: Double? {
        guard let s0 = sensor0.optionalValue else {
            return sensor1.optionalValue
        }
        guard let s1 = sensor1.optionalValue else {
            return s0
        }
        return (s0 + s1) / 2
    }

    var displayText: String {
        "\(sensor0.text) / \(sensor1.text)"
    }
}

extension TemperatureMeasurement {
    
    init(sensor0: TemperatureValue, sensor1: TemperatureValue, date: Date) {
        self.sensor0 = sensor0
        self.sensor1 = sensor1
        self.id = date.seconds
    }
}

extension TemperatureMeasurement: Codable {
    
    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        self.sensor0 = .init(byte: try container.decode(UInt8.self))
        self.sensor1 = .init(byte: try container.decode(UInt8.self))
        self.id = try container.decode(Int.self)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.unkeyedContainer()
        try container.encode(sensor0.byte)
        try container.encode(sensor1.byte)
        try container.encode(id)
    }
}

extension TemperatureMeasurement: Comparable {
    
    static func < (lhs: TemperatureMeasurement, rhs: TemperatureMeasurement) -> Bool {
        lhs.id < rhs.id
    }
    
    static func == (lhs: TemperatureMeasurement, rhs: TemperatureMeasurement) -> Bool {
        lhs.id == rhs.id
    }
}

extension Array where Element == TemperatureMeasurement {
    
    func maximumValue() -> Double? {
        compactMap { $0.maximumValue }.max()
    }
    
    func minimumValue() -> Double? {
        compactMap { $0.minimumValue }.min()
    }
}

private extension TemperatureValue {

    init(value: Double?) {
        if let value {
            self = .value(value)
        } else {
            self = .notFound
        }
    }
}

extension TemperatureMeasurement {

    static let mockData: [TemperatureMeasurement] = {
        let temps: [(Double?, Double?)] = [
            (20, 14),
            (20, 13.5),
            (20.5, 13.5),
            (20.5, 13.5),
            (21, 14),
            (21, 14),
            (nil, 14.5),
            (nil, 14),
            (nil, 14.5),
            (nil, 14),
            (nil, 14),
            (nil, 14.5),
            (nil, 15),
            (5.0, 15),
            (4.5, 15.5),
            (4.5, 16),
            (4.0, 16.5),
            (3.0, 17),
            (3.0, 19),
            (2.5, 20),
            (2.5, 20.5),
            (2.0, 20.5),
            (1.0, 20.5),
            (0.5, 20.5),
            (0.0, 20),
            (0.0, 20),
            (-1.0, 21.0),
            (-0.5, 21.0),
            (-3.0, 21.0),
            (-3.5, 20.5),
            (-4.0, 20.5),
            (-5.0, 20.0),
            (-5.0, nil),
            (-5.5, nil),
            (-5.0, nil),
            (-5.5, nil),
            (-6.0, nil),
            (-5.0, nil),
            (nil, nil),
            (nil, nil),
            (nil, nil),
            (-5.0, nil),
            (-4.5, nil),
            (-4.0, 23.0),
            (5.0, 24.0),
            (7.0, 25.0),
            (8.0, 25.5),
            (8.5, 25.5),
            (10.0, 25.5),
            (10.5, 24.0),
            (10.5, 24.0),
            (10.5, 24.5),
            (12.0, 23.5),
            (12.5, 24.0),
            (12.0, 23.5),
            (14.0, 24.0),
            (15.0, 25.0),
            (15.0, 25.0),
            (15.5, 25.0),
            (15.0, 25.0),
        ]
        
        let seconds = Date().seconds
        return temps.enumerated().map {
            TemperatureMeasurement(
                sensor0: .init(value: $0.element.0),
                sensor1: .init(value: $0.element.1),
                id: seconds + ($0.offset - temps.count) * 60)
        }
    }()
}
