//
//  ScenarioPlannerFactoryTests.swift
//  SportsTimeTests
//
//  TDD specification tests for ScenarioPlannerFactory.
//

import Testing
import CoreLocation
@testable import SportsTime

@Suite("ScenarioPlannerFactory")
@MainActor
struct ScenarioPlannerFactoryTests {

    // MARK: - Specification Tests: planner(for:)

    @Test("planner: followTeamId set returns ScenarioDPlanner")
    func planner_followTeamId_returnsScenarioD() {
        let prefs = TripPreferences(
            planningMode: .followTeam,
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1,
            followTeamId: "team-123"
        )

        let request = makeRequest(preferences: prefs)
        let planner = ScenarioPlannerFactory.planner(for: request)

        #expect(planner is ScenarioDPlanner)
    }

    @Test("planner: selectedGames not empty returns ScenarioBPlanner")
    func planner_selectedGames_returnsScenarioB() {
        let game = makeGame()
        let prefs = TripPreferences(
            planningMode: .gameFirst,
            sports: [.mlb],
            mustSeeGameIds: [game.id],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
        )

        let request = makeRequest(preferences: prefs, games: [game])
        let planner = ScenarioPlannerFactory.planner(for: request)

        #expect(planner is ScenarioBPlanner)
    }

    @Test("planner: start and end locations returns ScenarioCPlanner")
    func planner_startEndLocations_returnsScenarioC() {
        let nycCoord = CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855)
        let laCoord = CLLocationCoordinate2D(latitude: 34.0430, longitude: -118.2673)

        let prefs = TripPreferences(
            planningMode: .locations,
            startLocation: LocationInput(name: "NYC", coordinate: nycCoord),
            endLocation: LocationInput(name: "LA", coordinate: laCoord),
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
        )

        let request = makeRequest(preferences: prefs)
        let planner = ScenarioPlannerFactory.planner(for: request)

        #expect(planner is ScenarioCPlanner)
    }

    @Test("planner: date range only returns ScenarioAPlanner")
    func planner_dateRangeOnly_returnsScenarioA() {
        let prefs = TripPreferences(
            planningMode: .dateRange,
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
        )

        let request = makeRequest(preferences: prefs)
        let planner = ScenarioPlannerFactory.planner(for: request)

        #expect(planner is ScenarioAPlanner)
    }

    @Test("planner: priority is D > B > C > A")
    func planner_priority_DoverBoverCoverA() {
        // If all conditions are met, followTeamId wins
        let game = makeGame()
        let nycCoord = CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855)
        let laCoord = CLLocationCoordinate2D(latitude: 34.0430, longitude: -118.2673)

        let prefs = TripPreferences(
            planningMode: .followTeam,
            startLocation: LocationInput(name: "NYC", coordinate: nycCoord),
            endLocation: LocationInput(name: "LA", coordinate: laCoord),  // C condition
            sports: [.mlb],
            mustSeeGameIds: [game.id],  // B condition
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1,
            followTeamId: "team-123"  // D condition
        )

        let request = makeRequest(preferences: prefs, games: [game])
        let planner = ScenarioPlannerFactory.planner(for: request)

        #expect(planner is ScenarioDPlanner, "D should take priority")
    }

    @Test("planner: B takes priority over C")
    func planner_priority_BoverC() {
        // If B and C conditions met but not D, B wins
        let game = makeGame()
        let nycCoord = CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855)
        let laCoord = CLLocationCoordinate2D(latitude: 34.0430, longitude: -118.2673)

        let prefs = TripPreferences(
            planningMode: .gameFirst,
            startLocation: LocationInput(name: "NYC", coordinate: nycCoord),
            endLocation: LocationInput(name: "LA", coordinate: laCoord),  // C condition
            sports: [.mlb],
            mustSeeGameIds: [game.id],  // B condition
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
            // followTeamId: nil by default - Not D
        )

        let request = makeRequest(preferences: prefs, games: [game])
        let planner = ScenarioPlannerFactory.planner(for: request)

        #expect(planner is ScenarioBPlanner, "B should take priority over C")
    }

    // MARK: - Specification Tests: classify()

    @Test("classify: followTeamId returns scenarioD")
    func classify_followTeamId_returnsScenarioD() {
        let prefs = TripPreferences(
            planningMode: .followTeam,
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1,
            followTeamId: "team-123"
        )

        let request = makeRequest(preferences: prefs)
        let scenario = ScenarioPlannerFactory.classify(request)

        #expect(scenario == .scenarioD)
    }

    @Test("classify: selectedGames returns scenarioB")
    func classify_selectedGames_returnsScenarioB() {
        let game = makeGame()
        let prefs = TripPreferences(
            planningMode: .gameFirst,
            sports: [.mlb],
            mustSeeGameIds: [game.id],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
        )

        let request = makeRequest(preferences: prefs, games: [game])
        let scenario = ScenarioPlannerFactory.classify(request)

        #expect(scenario == .scenarioB)
    }

    @Test("classify: startEndLocations returns scenarioC")
    func classify_startEndLocations_returnsScenarioC() {
        let nycCoord = CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855)
        let laCoord = CLLocationCoordinate2D(latitude: 34.0430, longitude: -118.2673)

        let prefs = TripPreferences(
            planningMode: .locations,
            startLocation: LocationInput(name: "NYC", coordinate: nycCoord),
            endLocation: LocationInput(name: "LA", coordinate: laCoord),
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
        )

        let request = makeRequest(preferences: prefs)
        let scenario = ScenarioPlannerFactory.classify(request)

        #expect(scenario == .scenarioC)
    }

    @Test("classify: dateRangeOnly returns scenarioA")
    func classify_dateRangeOnly_returnsScenarioA() {
        let prefs = TripPreferences(
            planningMode: .dateRange,
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
        )

        let request = makeRequest(preferences: prefs)
        let scenario = ScenarioPlannerFactory.classify(request)

        #expect(scenario == .scenarioA)
    }

    // MARK: - Property Tests

    @Test("Property: planner and classify are consistent")
    func property_plannerAndClassifyConsistent() {
        // Scenario A
        let prefsA = TripPreferences(
            planningMode: .dateRange,
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1
        )
        let requestA = makeRequest(preferences: prefsA)
        let plannerA = ScenarioPlannerFactory.planner(for: requestA)
        let classifyA = ScenarioPlannerFactory.classify(requestA)
        #expect(plannerA is ScenarioAPlanner && classifyA == .scenarioA)

        // Scenario D
        let prefsD = TripPreferences(
            planningMode: .followTeam,
            sports: [.mlb],
            startDate: Date(),
            endDate: Date().addingTimeInterval(86400 * 7),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 1,
            followTeamId: "team-123"
        )
        let requestD = makeRequest(preferences: prefsD)
        let plannerD = ScenarioPlannerFactory.planner(for: requestD)
        let classifyD = ScenarioPlannerFactory.classify(requestD)
        #expect(plannerD is ScenarioDPlanner && classifyD == .scenarioD)
    }

    // MARK: - Helper Methods

    private func makeRequest(
        preferences: TripPreferences,
        games: [Game] = []
    ) -> PlanningRequest {
        PlanningRequest(
            preferences: preferences,
            availableGames: games,
            teams: [:],
            stadiums: [:]
        )
    }

    private func makeGame() -> Game {
        Game(
            id: "game-\(UUID().uuidString)",
            homeTeamId: "team1",
            awayTeamId: "team2",
            stadiumId: "stadium1",
            dateTime: Date(),
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )
    }
}
