//
//  EVChargingServiceTests.swift
//  SportsTimeTests
//
//  TDD specification tests for EVChargingService types.
//

import Testing
import Foundation
@testable import SportsTime

// MARK: - ChargerType Detection Tests

@Suite("ChargerType Detection")
struct ChargerTypeDetectionTests {

    // MARK: - Test Helper

    // Note: detectChargerType and estimateChargeTime are private in EVChargingService
    // These tests document expected behavior for the ChargerType enum

    // MARK: - Specification Tests: ChargerType Cases

    /// - Expected Behavior: ChargerType has three cases
    @Test("ChargerType: has supercharger case")
    func chargerType_supercharger() {
        let type = ChargerType.supercharger
        #expect(type == .supercharger)
    }

    @Test("ChargerType: has dcFast case")
    func chargerType_dcFast() {
        let type = ChargerType.dcFast
        #expect(type == .dcFast)
    }

    @Test("ChargerType: has level2 case")
    func chargerType_level2() {
        let type = ChargerType.level2
        #expect(type == .level2)
    }

    // MARK: - Invariant Tests

    /// - Invariant: All charger types are distinct
    @Test("Invariant: all charger types are distinct")
    func invariant_distinctTypes() {
        let types: [ChargerType] = [.supercharger, .dcFast, .level2]
        let uniqueTypes = Set(types)
        #expect(types.count == uniqueTypes.count)
    }
}

// MARK: - EVChargingStop Tests

@Suite("EVChargingStop")
struct EVChargingStopTests {

    // MARK: - Test Data

    private func makeStop(
        name: String = "Tesla Supercharger",
        chargerType: ChargerType = .supercharger,
        estimatedChargeTime: TimeInterval = 25 * 60
    ) -> EVChargingStop {
        EVChargingStop(
            name: name,
            location: LocationInput(name: name, coordinate: nil, address: nil),
            chargerType: chargerType,
            estimatedChargeTime: estimatedChargeTime
        )
    }

    // MARK: - Specification Tests: Properties

    @Test("EVChargingStop: stores name")
    func evChargingStop_name() {
        let stop = makeStop(name: "Test Charger")
        #expect(stop.name == "Test Charger")
    }

    @Test("EVChargingStop: stores chargerType")
    func evChargingStop_chargerType() {
        let stop = makeStop(chargerType: .dcFast)
        #expect(stop.chargerType == .dcFast)
    }

    @Test("EVChargingStop: stores estimatedChargeTime")
    func evChargingStop_estimatedChargeTime() {
        let stop = makeStop(estimatedChargeTime: 30 * 60)
        #expect(stop.estimatedChargeTime == 30 * 60)
    }

    @Test("EVChargingStop: stores location")
    func evChargingStop_location() {
        let stop = makeStop(name: "Test Location")
        #expect(stop.location.name == "Test Location")
    }

    // MARK: - Specification Tests: Expected Charge Times

    /// - Expected Behavior: Supercharger takes ~25 minutes
    @Test("Expected charge time: supercharger ~25 minutes")
    func expectedChargeTime_supercharger() {
        // Based on EVChargingService.estimateChargeTime implementation
        let expectedTime: TimeInterval = 25 * 60
        let stop = makeStop(chargerType: .supercharger, estimatedChargeTime: expectedTime)
        #expect(stop.estimatedChargeTime == 25 * 60)
    }

    /// - Expected Behavior: DC Fast takes ~30 minutes
    @Test("Expected charge time: dcFast ~30 minutes")
    func expectedChargeTime_dcFast() {
        let expectedTime: TimeInterval = 30 * 60
        let stop = makeStop(chargerType: .dcFast, estimatedChargeTime: expectedTime)
        #expect(stop.estimatedChargeTime == 30 * 60)
    }

    /// - Expected Behavior: Level 2 takes ~2 hours
    @Test("Expected charge time: level2 ~2 hours")
    func expectedChargeTime_level2() {
        let expectedTime: TimeInterval = 120 * 60
        let stop = makeStop(chargerType: .level2, estimatedChargeTime: expectedTime)
        #expect(stop.estimatedChargeTime == 120 * 60)
    }

    // MARK: - Invariant Tests

    /// - Invariant: Each stop has unique id
    @Test("Invariant: each stop has unique id")
    func invariant_uniqueId() {
        let stop1 = makeStop()
        let stop2 = makeStop()
        #expect(stop1.id != stop2.id)
    }
}

// MARK: - Charger Detection Behavior Tests

@Suite("Charger Detection Behavior")
struct ChargerDetectionBehaviorTests {

    // These tests document the expected name → charger type mapping
    // based on the detectChargerType implementation

    // MARK: - Specification Tests: Name Detection Rules

    /// - Expected Behavior: "Tesla" or "Supercharger" → .supercharger
    @Test("Detection rule: Tesla names should map to supercharger")
    func detectionRule_tesla() {
        // Expected names that should return .supercharger:
        // - "Tesla Supercharger"
        // - "Supercharger"
        // - "Tesla Station"
        let teslaNames = ["Tesla Supercharger", "Supercharger", "Tesla Station"]
        for name in teslaNames {
            let lowercased = name.lowercased()
            let isTesla = lowercased.contains("tesla") || lowercased.contains("supercharger")
            #expect(isTesla, "'\(name)' should be detected as Tesla/Supercharger")
        }
    }

    /// - Expected Behavior: DC Fast keywords → .dcFast
    @Test("Detection rule: DC Fast keywords should map to dcFast")
    func detectionRule_dcFast() {
        // Expected names that should return .dcFast:
        // - "DC Fast Charging"
        // - "DCFC Station"
        // - "CCS Charger"
        // - "CHAdeMO"
        // - "Electrify America"
        // - "EVgo"
        let dcFastKeywords = ["dc fast", "dcfc", "ccs", "chademo", "electrify america", "evgo"]
        let testNames = ["DC Fast Charging", "DCFC Station", "CCS Charger", "CHAdeMO", "Electrify America", "EVgo Station"]

        for name in testNames {
            let lowercased = name.lowercased()
            let isDCFast = dcFastKeywords.contains { lowercased.contains($0) }
            #expect(isDCFast, "'\(name)' should be detected as DC Fast")
        }
    }

    /// - Expected Behavior: Other names → .level2 (default)
    @Test("Detection rule: Unknown names default to level2")
    func detectionRule_level2Default() {
        // Names without specific keywords should default to Level 2
        let genericNames = ["ChargePoint", "Blink", "Generic EV Charger", "Unknown Station"]

        for name in genericNames {
            let lowercased = name.lowercased()
            let isTesla = lowercased.contains("tesla") || lowercased.contains("supercharger")
            let isDCFast = ["dc fast", "dcfc", "ccs", "chademo", "electrify america", "evgo"]
                .contains { lowercased.contains($0) }

            #expect(!isTesla && !isDCFast, "'\(name)' should not match Tesla or DC Fast")
        }
    }

    // MARK: - Invariant Tests

    /// - Invariant: Detection is case-insensitive
    @Test("Invariant: detection is case-insensitive")
    func invariant_caseInsensitive() {
        let variations = ["TESLA", "Tesla", "tesla", "TeSLa"]
        for name in variations {
            let isTesla = name.lowercased().contains("tesla")
            #expect(isTesla, "'\(name)' should match 'tesla' case-insensitively")
        }
    }
}
