//
//  TripPlanningEngineTests.swift
//  SportsTimeTests
//
//  TDD specification tests for TripPlanningEngine.
//

import Testing
import Foundation
import CoreLocation
@testable import SportsTime

@Suite("TripPlanningEngine")
struct TripPlanningEngineTests {

    // MARK: - Test Data

    private let nycCoord = CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855)
    private let bostonCoord = CLLocationCoordinate2D(latitude: 42.3467, longitude: -71.0972)
    private let chicagoCoord = CLLocationCoordinate2D(latitude: 41.8827, longitude: -87.6233)
    private let laCoord = CLLocationCoordinate2D(latitude: 34.0430, longitude: -118.2673)

    // MARK: - Specification Tests: Planning Mode Selection

    @Test("planningMode: dateRange is valid mode")
    func planningMode_dateRange() {
        let prefs = TripPreferences(
            planningMode: .dateRange,
            sports: [.mlb]
        )
        #expect(prefs.planningMode == .dateRange)
    }

    @Test("planningMode: gameFirst is valid mode")
    func planningMode_gameFirst() {
        let prefs = TripPreferences(
            planningMode: .gameFirst,
            sports: [.mlb],
            mustSeeGameIds: ["game1"]
        )
        #expect(prefs.planningMode == .gameFirst)
    }

    @Test("planningMode: followTeam is valid mode")
    func planningMode_followTeam() {
        let prefs = TripPreferences(
            planningMode: .followTeam,
            sports: [.mlb],
            followTeamId: "yankees"
        )
        #expect(prefs.planningMode == .followTeam)
    }

    @Test("planningMode: locations is valid mode")
    func planningMode_locations() {
        let prefs = TripPreferences(
            planningMode: .locations,
            startLocation: LocationInput(name: "Chicago", coordinate: chicagoCoord),
            endLocation: LocationInput(name: "New York", coordinate: nycCoord),
            sports: [.mlb]
        )
        #expect(prefs.planningMode == .locations)
    }

    // MARK: - Specification Tests: Driving Constraints

    @Test("DrivingConstraints: calculates maxDailyDrivingHours correctly")
    func drivingConstraints_maxDailyHours() {
        let constraints = DrivingConstraints(numberOfDrivers: 2, maxHoursPerDriverPerDay: 6.0)
        #expect(constraints.maxDailyDrivingHours == 12.0)
    }

    @Test("DrivingConstraints: clamps negative drivers to 1")
    func drivingConstraints_clampsNegativeDrivers() {
        let constraints = DrivingConstraints(numberOfDrivers: -5, maxHoursPerDriverPerDay: 8.0)
        #expect(constraints.numberOfDrivers == 1)
        #expect(constraints.maxDailyDrivingHours >= 1.0)
    }

    @Test("DrivingConstraints: clamps zero hours to minimum")
    func drivingConstraints_clampsZeroHours() {
        let constraints = DrivingConstraints(numberOfDrivers: 1, maxHoursPerDriverPerDay: 0)
        #expect(constraints.maxHoursPerDriverPerDay == 1.0)
    }

    // MARK: - Specification Tests: Trip Preferences Computed Properties

    @Test("totalDriverHoursPerDay: defaults to 8 hours when nil")
    func totalDriverHoursPerDay_default() {
        let prefs = TripPreferences(
            numberOfDrivers: 1,
            maxDrivingHoursPerDriver: nil
        )
        #expect(prefs.totalDriverHoursPerDay == 8.0)
    }

    @Test("totalDriverHoursPerDay: multiplies by number of drivers")
    func totalDriverHoursPerDay_multipleDrivers() {
        let prefs = TripPreferences(
            numberOfDrivers: 2,
            maxDrivingHoursPerDriver: 6.0
        )
        #expect(prefs.totalDriverHoursPerDay == 12.0)
    }

    @Test("effectiveTripDuration: uses explicit tripDuration when set")
    func effectiveTripDuration_explicit() {
        let prefs = TripPreferences(
            tripDuration: 5
        )
        #expect(prefs.effectiveTripDuration == 5)
    }

    @Test("effectiveTripDuration: calculates from date range when tripDuration is nil")
    func effectiveTripDuration_calculated() {
        let calendar = Calendar.current
        let startDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
        let endDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 22))!

        let prefs = TripPreferences(
            startDate: startDate,
            endDate: endDate,
            tripDuration: nil
        )
        #expect(prefs.effectiveTripDuration == 7)
    }

    // MARK: - Invariant Tests

    @Test("Invariant: totalDriverHoursPerDay > 0")
    func invariant_totalDriverHoursPositive() {
        let prefs1 = TripPreferences(numberOfDrivers: 1)
        #expect(prefs1.totalDriverHoursPerDay > 0)

        let prefs2 = TripPreferences(numberOfDrivers: 3, maxDrivingHoursPerDriver: 4)
        #expect(prefs2.totalDriverHoursPerDay > 0)
    }

    @Test("Invariant: effectiveTripDuration >= 1")
    func invariant_effectiveTripDurationMinimum() {
        let testCases: [Int?] = [nil, 1, 5, 10]

        for duration in testCases {
            let prefs = TripPreferences(tripDuration: duration)
            #expect(prefs.effectiveTripDuration >= 1)
        }
    }

    // MARK: - Helper Methods

    private func makeStadium(
        id: String,
        city: String,
        coordinate: CLLocationCoordinate2D
    ) -> Stadium {
        Stadium(
            id: id,
            name: "\(city) Stadium",
            city: city,
            state: "XX",
            latitude: coordinate.latitude,
            longitude: coordinate.longitude,
            capacity: 40000,
            sport: .mlb
        )
    }

    private func makeGame(
        id: String,
        stadiumId: String,
        dateTime: Date
    ) -> Game {
        Game(
            id: id,
            homeTeamId: "team1",
            awayTeamId: "team2",
            stadiumId: stadiumId,
            dateTime: dateTime,
            sport: .mlb,
            season: "2026"
        )
    }
}
