//
//  TherapySettingsTests.swift
//  LoopKitTests
//
//  Created by Anna Quinlan on 7/27/20.
//  Copyright © 2020 LoopKit Authors. All rights reserved.
//

import XCTest
import HealthKit
@testable import LoopKit

class TherapySettingsCodableTests: XCTestCase {
    private let dateFormatter = ISO8601DateFormatter()

    private let encoder: JSONEncoder = {
        let encoder = JSONEncoder()
        encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
        encoder.dateEncodingStrategy = .iso8601
        return encoder
    }()
    
    private let decoder: JSONDecoder = {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        return decoder
    }()
    
    let encodedString = """
    {
      "basalRateSchedule" : {
        "items" : [
          {
            "startTime" : 0,
            "value" : 1
          },
          {
            "startTime" : 28800,
            "value" : 1.125
          },
          {
            "startTime" : 36000,
            "value" : 1.25
          },
          {
            "startTime" : 43200,
            "value" : 1.5
          },
          {
            "startTime" : 50400,
            "value" : 1.25
          },
          {
            "startTime" : 57600,
            "value" : 1.5
          },
          {
            "startTime" : 64800,
            "value" : 1.25
          },
          {
            "startTime" : 75600,
            "value" : 1
          }
        ],
        "referenceTimeInterval" : 0,
        "repeatInterval" : 86400,
        "timeZone" : {
          "identifier" : "GMT-0700"
        }
      },
      "carbRatioSchedule" : {
        "unit" : "g",
        "valueSchedule" : {
          "items" : [
            {
              "startTime" : 0,
              "value" : 10
            },
            {
              "startTime" : 28800,
              "value" : 12
            },
            {
              "startTime" : 36000,
              "value" : 9
            },
            {
              "startTime" : 43200,
              "value" : 10
            },
            {
              "startTime" : 50400,
              "value" : 11
            },
            {
              "startTime" : 57600,
              "value" : 12
            },
            {
              "startTime" : 64800,
              "value" : 8
            },
            {
              "startTime" : 75600,
              "value" : 10
            }
          ],
          "referenceTimeInterval" : 0,
          "repeatInterval" : 86400,
          "timeZone" : {
            "identifier" : "GMT-0700"
          }
        }
      },
      "correctionRangeOverrides" : {
        "preMealRange" : {
          "bloodGlucoseUnit" : "mg/dL",
          "range" : {
            "maxValue" : 90,
            "minValue" : 80
          }
        },
        "workoutRange" : {
          "bloodGlucoseUnit" : "mg/dL",
          "range" : {
            "maxValue" : 140,
            "minValue" : 130
          }
        }
      },
      "defaultRapidActingModel" : "rapidActingAdult",
      "glucoseTargetRangeSchedule" : {
        "rangeSchedule" : {
          "unit" : "mg/dL",
          "valueSchedule" : {
            "items" : [
              {
                "startTime" : 0,
                "value" : {
                  "maxValue" : 110,
                  "minValue" : 100
                }
              },
              {
                "startTime" : 28800,
                "value" : {
                  "maxValue" : 105,
                  "minValue" : 95
                }
              },
              {
                "startTime" : 50400,
                "value" : {
                  "maxValue" : 105,
                  "minValue" : 95
                }
              },
              {
                "startTime" : 57600,
                "value" : {
                  "maxValue" : 110,
                  "minValue" : 100
                }
              },
              {
                "startTime" : 64800,
                "value" : {
                  "maxValue" : 100,
                  "minValue" : 90
                }
              },
              {
                "startTime" : 75600,
                "value" : {
                  "maxValue" : 120,
                  "minValue" : 110
                }
              }
            ],
            "referenceTimeInterval" : 0,
            "repeatInterval" : 86400,
            "timeZone" : {
              "identifier" : "GMT-0700"
            }
          }
        }
      },
      "insulinSensitivitySchedule" : {
        "unit" : "mg/dL",
        "valueSchedule" : {
          "items" : [
            {
              "startTime" : 0,
              "value" : 45
            },
            {
              "startTime" : 28800,
              "value" : 40
            },
            {
              "startTime" : 36000,
              "value" : 35
            },
            {
              "startTime" : 43200,
              "value" : 30
            },
            {
              "startTime" : 50400,
              "value" : 35
            },
            {
              "startTime" : 57600,
              "value" : 40
            }
          ],
          "referenceTimeInterval" : 0,
          "repeatInterval" : 86400,
          "timeZone" : {
            "identifier" : "GMT-0700"
          }
        }
      },
      "maximumBasalRatePerHour" : 3,
      "maximumBolus" : 5,
      "suspendThreshold" : {
        "unit" : "mg/dL",
        "value" : 80
      }
    }
    """
    
    func testInsulinModelEncoding() throws {
        let adult = ExponentialInsulinModelPreset.rapidActingAdult
        let child = ExponentialInsulinModelPreset.rapidActingChild
        
        XCTAssertEqual("""
        "rapidActingAdult"
        """, String(data: try encoder.encode(adult), encoding: .utf8)!)
        XCTAssertEqual("""
        "rapidActingChild"
        """, String(data: try encoder.encode(child), encoding: .utf8)!)
    }

    func testTherapySettingEncoding() throws {
        let original = TherapySettings.test
        let data = try encoder.encode(original)
        XCTAssertEqual(encodedString, String(data: data, encoding: .utf8)!)
    }

    func testTherapySettingDecoding() throws {
        let data = encodedString.data(using: .utf8)!
        let decoded = try decoder.decode(TherapySettings.self, from: data)
        let expected = TherapySettings.test

        XCTAssertEqual(expected, decoded)

        XCTAssertEqual(decoded.basalRateSchedule, expected.basalRateSchedule)
        XCTAssertEqual(decoded.insulinSensitivitySchedule, expected.insulinSensitivitySchedule)
        XCTAssertEqual(decoded.correctionRangeOverrides, expected.correctionRangeOverrides)
        XCTAssertEqual(decoded.maximumBolus, expected.maximumBolus)
        XCTAssertEqual(decoded.maximumBasalRatePerHour, expected.maximumBasalRatePerHour)
        XCTAssertEqual(decoded.suspendThreshold, expected.suspendThreshold)
        XCTAssertEqual(decoded.carbRatioSchedule, expected.carbRatioSchedule)
        XCTAssertEqual(decoded.defaultRapidActingModel, expected.defaultRapidActingModel)
        XCTAssertEqual(decoded.glucoseTargetRangeSchedule, expected.glucoseTargetRangeSchedule)
    }
}

fileprivate extension TherapySettings {
    static var test: TherapySettings {
        let timeZone = TimeZone(secondsFromGMT: -25200)
        let glucoseTargetRangeSchedule =  GlucoseRangeSchedule(
            rangeSchedule: DailyQuantitySchedule(unit: .milligramsPerDeciliter,
                                                 dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: DoubleRange(minValue: 100.0, maxValue: 110.0)),
                                                              RepeatingScheduleValue(startTime: .hours(8), value: DoubleRange(minValue: 95.0, maxValue: 105.0)),
                                                              RepeatingScheduleValue(startTime: .hours(14), value: DoubleRange(minValue: 95.0, maxValue: 105.0)),
                                                              RepeatingScheduleValue(startTime: .hours(16), value: DoubleRange(minValue: 100.0, maxValue: 110.0)),
                                                              RepeatingScheduleValue(startTime: .hours(18), value: DoubleRange(minValue: 90.0, maxValue: 100.0)),
                                                              RepeatingScheduleValue(startTime: .hours(21), value: DoubleRange(minValue: 110.0, maxValue: 120.0))],
                                                 timeZone: timeZone)!)
        let basalRateSchedule = BasalRateSchedule(
            dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: 1.0),
                         RepeatingScheduleValue(startTime: .hours(8), value: 1.125),
                         RepeatingScheduleValue(startTime: .hours(10), value: 1.25),
                         RepeatingScheduleValue(startTime: .hours(12), value: 1.5),
                         RepeatingScheduleValue(startTime: .hours(14), value: 1.25),
                         RepeatingScheduleValue(startTime: .hours(16), value: 1.5),
                         RepeatingScheduleValue(startTime: .hours(18), value: 1.25),
                         RepeatingScheduleValue(startTime: .hours(21), value: 1.0)],
            timeZone: timeZone)!
        let insulinSensitivitySchedule = InsulinSensitivitySchedule(
            unit: .milligramsPerDeciliter,
            dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: 45.0),
                         RepeatingScheduleValue(startTime: .hours(8), value: 40.0),
                         RepeatingScheduleValue(startTime: .hours(10), value: 35.0),
                         RepeatingScheduleValue(startTime: .hours(12), value: 30.0),
                         RepeatingScheduleValue(startTime: .hours(14), value: 35.0),
                         RepeatingScheduleValue(startTime: .hours(16), value: 40.0)],
            timeZone: timeZone)!
        let carbRatioSchedule = CarbRatioSchedule(
            unit: .gram(),

            dailyItems: [RepeatingScheduleValue(startTime: .hours(0), value: 10.0),
                         RepeatingScheduleValue(startTime: .hours(8), value: 12.0),
                         RepeatingScheduleValue(startTime: .hours(10), value: 9.0),
                         RepeatingScheduleValue(startTime: .hours(12), value: 10.0),
                         RepeatingScheduleValue(startTime: .hours(14), value: 11.0),
                         RepeatingScheduleValue(startTime: .hours(16), value: 12.0),
                         RepeatingScheduleValue(startTime: .hours(18), value: 8.0),
                         RepeatingScheduleValue(startTime: .hours(21), value: 10.0)],
            timeZone: timeZone)!
        let correctionRangeOverrides = CorrectionRangeOverrides(
            preMeal: DoubleRange(minValue: 80.0, maxValue: 90.0),
            workout: DoubleRange(minValue: 130.0, maxValue: 140.0),
            unit: .milligramsPerDeciliter)

        return TherapySettings(
            glucoseTargetRangeSchedule: glucoseTargetRangeSchedule,
            correctionRangeOverrides: correctionRangeOverrides,
            maximumBasalRatePerHour: 3,
            maximumBolus: 5,
            suspendThreshold: GlucoseThreshold(unit: .milligramsPerDeciliter, value: 80),
            insulinSensitivitySchedule: insulinSensitivitySchedule,
            carbRatioSchedule: carbRatioSchedule,
            basalRateSchedule: basalRateSchedule,
            defaultRapidActingModel: ExponentialInsulinModelPreset.rapidActingAdult
        )
    }
}
