import Foundation
import Testing
@testable import Trio

@Suite("Calculate IOB Tests") struct CalculateIobTests {
    // Helper function to create a basic treatment
    func createTreatment(insulin: Decimal, timestamp: Date) -> ComputedPumpHistoryEvent {
        ComputedPumpHistoryEvent.forTest(
            type: .bolus,
            timestamp: timestamp,
            insulin: insulin
        )
    }

    // Helper function to create a basic profile
    func createProfile(
        curve: InsulinCurve = .rapidActing,
        useCustomPeakTime: Bool = false,
        insulinPeakTime: Decimal = 0
    ) -> Profile {
        var profile = Profile()
        profile.curve = curve
        profile.useCustomPeakTime = useCustomPeakTime
        profile.insulinPeakTime = insulinPeakTime
        profile.dia = 3
        return profile
    }

    @Test("should return nil when treatment has no insulin") func returnNilForNoInsulin() async throws {
        let treatment = ComputedPumpHistoryEvent.forTest(
            type: .bolus,
            timestamp: Date()
        )

        let result = try IobCalculation.iobCalc(
            treatment: treatment,
            time: Date(),
            dia: 3,
            profile: createProfile()
        )

        #expect(result == nil)
    }

    @Test("should calculate IOB with default rapid-acting settings") func calculateDefaultRapidActing() async throws {
        let now = Date()
        let thirtyMinsAgo = now - 30.minutesToSeconds
        let treatment = createTreatment(insulin: 2, timestamp: thirtyMinsAgo)

        let result = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: createProfile()
        )

        #expect(result != nil)
        #expect(result!.activityContrib.isApproximatelyEqual(to: 0.0115, epsilon: 0.0001))
        #expect(result!.iobContrib.isApproximatelyEqual(to: 1.8085, epsilon: 0.0001))
    }

    @Test("should calculate IOB with custom peak time for rapid-acting insulin") func calculateCustomPeakRapidActing() async throws {
        let now = Date()
        let thirtyMinsAgo = now - 30.minutesToSeconds
        let treatment = createTreatment(insulin: 2, timestamp: thirtyMinsAgo)

        let profile = createProfile(
            curve: .rapidActing,
            useCustomPeakTime: true,
            insulinPeakTime: 100
        )

        let result = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: profile
        )

        #expect(result != nil)
        #expect(result!.activityContrib.isApproximatelyEqual(to: 0.0079, epsilon: 0.0001))
        #expect(result!.iobContrib.isApproximatelyEqual(to: 1.8763, epsilon: 0.0001))
    }

    @Test("should handle peak time limits for rapid-acting insulin") func handlePeakTimeLimitsRapidActing() async throws {
        let now = Date()
        let thirtyMinsAgo = now - 30.minutesToSeconds
        let treatment = createTreatment(insulin: 2, timestamp: thirtyMinsAgo)

        // Test upper limit (120)
        let profileHigh = createProfile(
            curve: .rapidActing,
            useCustomPeakTime: true,
            insulinPeakTime: 150
        )
        let resultHigh = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: profileHigh
        )
        #expect(resultHigh != nil)

        // Test lower limit (50)
        let profileLow = createProfile(
            curve: .rapidActing,
            useCustomPeakTime: true,
            insulinPeakTime: 30
        )
        let resultLow = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: profileLow
        )
        #expect(resultLow != nil)
    }

    @Test("should calculate IOB with ultra-rapid insulin") func calculateUltraRapid() async throws {
        let now = Date()
        let thirtyMinsAgo = now - 30.minutesToSeconds
        let treatment = createTreatment(insulin: 2, timestamp: thirtyMinsAgo)

        let profile = createProfile(curve: .ultraRapid)

        let result = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: profile
        )

        #expect(result != nil)
        #expect(result!.activityContrib.isApproximatelyEqual(to: 0.01569, epsilon: 0.0001))
        #expect(result!.iobContrib.isApproximatelyEqual(to: 1.7202, epsilon: 0.0001))
    }

    @Test("should handle peak time limits for ultra-rapid insulin") func handlePeakTimeLimitsUltraRapid() async throws {
        let now = Date()
        let thirtyMinsAgo = now - 30.minutesToSeconds
        let treatment = createTreatment(insulin: 2, timestamp: thirtyMinsAgo)

        // Test upper limit (100)
        let profileHigh = createProfile(
            curve: .ultraRapid,
            useCustomPeakTime: true,
            insulinPeakTime: 120
        )
        let resultHigh = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: profileHigh
        )
        #expect(resultHigh != nil)

        // Test lower limit (35)
        let profileLow = createProfile(
            curve: .ultraRapid,
            useCustomPeakTime: true,
            insulinPeakTime: 30
        )
        let resultLow = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: profileLow
        )
        #expect(resultLow != nil)
    }

    @Test("should handle insulin activity after DIA") func handleActivityAfterDIA() async throws {
        let now = Date()
        let fourHoursAgo = now - (4 * 60 * 60)
        let treatment = createTreatment(insulin: 2, timestamp: fourHoursAgo)

        let result = try IobCalculation.iobCalc(
            treatment: treatment,
            time: now,
            dia: 3,
            profile: createProfile()
        )

        #expect(result != nil)
        #expect(result?.activityContrib == 0)
        #expect(result?.iobContrib == 0)
    }
}
