//
//  TeamFirstIntegrationTests.swift
//  SportsTimeTests
//
//  Integration tests for Team-First planning mode.
//
//  These tests verify the end-to-end flow from team selection
//  to route generation, ensuring all components work together.
//

import Testing
import CoreLocation
@testable import SportsTime

@Suite("TeamFirst Integration")
struct TeamFirstIntegrationTests {

    // MARK: - Test Data

    private let planner = ScenarioEPlanner()

    // MLB stadiums with realistic coordinates
    private let yankeeStadiumCoord = CLLocationCoordinate2D(latitude: 40.8296, longitude: -73.9262)
    private let fenwayParkCoord = CLLocationCoordinate2D(latitude: 42.3467, longitude: -71.0972)
    private let citizensBankCoord = CLLocationCoordinate2D(latitude: 39.9061, longitude: -75.1665)

    // MARK: - E3. Full Flow Integration Tests

    @Test("Integration: 3 MLB teams returns top 10 routes")
    func integration_3MLBTeams_returnsTop10Routes() {
        let baseDate = Date()

        // Create realistic MLB stadiums
        let yankeeStadium = Stadium(
            id: "stadium_mlb_yankee_stadium",
            name: "Yankee Stadium",
            city: "New York",
            state: "NY",
            latitude: yankeeStadiumCoord.latitude,
            longitude: yankeeStadiumCoord.longitude,
            capacity: 47309,
            sport: .mlb
        )

        let fenwayPark = Stadium(
            id: "stadium_mlb_fenway_park",
            name: "Fenway Park",
            city: "Boston",
            state: "MA",
            latitude: fenwayParkCoord.latitude,
            longitude: fenwayParkCoord.longitude,
            capacity: 37755,
            sport: .mlb
        )

        let citizensBank = Stadium(
            id: "stadium_mlb_citizens_bank",
            name: "Citizens Bank Park",
            city: "Philadelphia",
            state: "PA",
            latitude: citizensBankCoord.latitude,
            longitude: citizensBankCoord.longitude,
            capacity: 42792,
            sport: .mlb
        )

        // Create teams
        let yankees = Team(
            id: "team_mlb_nyy",
            name: "Yankees",
            abbreviation: "NYY",
            sport: .mlb,
            city: "New York",
            stadiumId: "stadium_mlb_yankee_stadium"
        )

        let redsox = Team(
            id: "team_mlb_bos",
            name: "Red Sox",
            abbreviation: "BOS",
            sport: .mlb,
            city: "Boston",
            stadiumId: "stadium_mlb_fenway_park"
        )

        let phillies = Team(
            id: "team_mlb_phi",
            name: "Phillies",
            abbreviation: "PHI",
            sport: .mlb,
            city: "Philadelphia",
            stadiumId: "stadium_mlb_citizens_bank"
        )

        // Create games within a reasonable 6-day window (3 teams * 2 = 6 days)
        // For the window algorithm to find valid windows, games must span at least 6 days
        // Window check: windowEnd <= latestDay + 1, so with 6-day window from day 1,
        // windowEnd = day 7, so latestDay must be >= day 6
        // Day 1: Yankees home
        // Day 3: Red Sox home
        // Day 6: Phillies home (spans 6 days, window fits)
        let calendar = Calendar.current
        let day1 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 1))!
        let day3 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 3))!
        let day6 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 6))!

        let yankeesGame = Game(
            id: "game_mlb_2026_nyy_opp_0401",
            homeTeamId: "team_mlb_nyy",
            awayTeamId: "team_mlb_opp",
            stadiumId: "stadium_mlb_yankee_stadium",
            dateTime: day1,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        let redsoxGame = Game(
            id: "game_mlb_2026_bos_opp_0403",
            homeTeamId: "team_mlb_bos",
            awayTeamId: "team_mlb_opp",
            stadiumId: "stadium_mlb_fenway_park",
            dateTime: day3,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        let philliesGame = Game(
            id: "game_mlb_2026_phi_opp_0405",
            homeTeamId: "team_mlb_phi",
            awayTeamId: "team_mlb_opp",
            stadiumId: "stadium_mlb_citizens_bank",
            dateTime: day6,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        let prefs = TripPreferences(
            planningMode: .teamFirst,
            sports: [.mlb],
            startDate: baseDate,
            endDate: baseDate.addingTimeInterval(86400 * 30),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 2,
            selectedTeamIds: ["team_mlb_nyy", "team_mlb_bos", "team_mlb_phi"]
        )

        let request = PlanningRequest(
            preferences: prefs,
            availableGames: [yankeesGame, redsoxGame, philliesGame],
            teams: [
                "team_mlb_nyy": yankees,
                "team_mlb_bos": redsox,
                "team_mlb_phi": phillies
            ],
            stadiums: [
                "stadium_mlb_yankee_stadium": yankeeStadium,
                "stadium_mlb_fenway_park": fenwayPark,
                "stadium_mlb_citizens_bank": citizensBank
            ]
        )

        let result = planner.plan(request: request)

        guard case .success(let options) = result else {
            Issue.record("Expected success with 3 MLB teams in drivable region")
            return
        }

        // Verify we get routes (may be less than 10 due to limited game combinations)
        #expect(!options.isEmpty, "Should return at least one route")
        #expect(options.count <= 10, "Should return at most 10 routes")
    }

    @Test("Integration: each route visits all 3 stadiums")
    func integration_eachRouteVisitsAll3Stadiums() {
        let baseDate = Date()

        let yankeeStadium = makeStadium(
            id: "yankee-stadium",
            name: "Yankee Stadium",
            city: "New York",
            coordinate: yankeeStadiumCoord
        )
        let fenwayPark = makeStadium(
            id: "fenway-park",
            name: "Fenway Park",
            city: "Boston",
            coordinate: fenwayParkCoord
        )
        let citizensBank = makeStadium(
            id: "citizens-bank",
            name: "Citizens Bank Park",
            city: "Philadelphia",
            coordinate: citizensBankCoord
        )

        // Create multiple games per team to ensure routes can be found
        var games: [Game] = []

        // Yankees games
        for dayOffset in [1, 7, 14] {
            games.append(Game(
                id: "yankees-\(dayOffset)",
                homeTeamId: "yankees",
                awayTeamId: "opponent",
                stadiumId: "yankee-stadium",
                dateTime: baseDate.addingTimeInterval(Double(86400 * dayOffset)),
                sport: .mlb,
                season: "2026",
                isPlayoff: false
            ))
        }

        // Red Sox games
        for dayOffset in [2, 8, 15] {
            games.append(Game(
                id: "redsox-\(dayOffset)",
                homeTeamId: "redsox",
                awayTeamId: "opponent",
                stadiumId: "fenway-park",
                dateTime: baseDate.addingTimeInterval(Double(86400 * dayOffset)),
                sport: .mlb,
                season: "2026",
                isPlayoff: false
            ))
        }

        // Phillies games
        for dayOffset in [3, 9, 16] {
            games.append(Game(
                id: "phillies-\(dayOffset)",
                homeTeamId: "phillies",
                awayTeamId: "opponent",
                stadiumId: "citizens-bank",
                dateTime: baseDate.addingTimeInterval(Double(86400 * dayOffset)),
                sport: .mlb,
                season: "2026",
                isPlayoff: false
            ))
        }

        let prefs = TripPreferences(
            planningMode: .teamFirst,
            sports: [.mlb],
            startDate: baseDate,
            endDate: baseDate.addingTimeInterval(86400 * 30),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 2,
            selectedTeamIds: ["yankees", "redsox", "phillies"]
        )

        let request = PlanningRequest(
            preferences: prefs,
            availableGames: games,
            teams: [
                "yankees": makeTeam(id: "yankees", name: "Yankees"),
                "redsox": makeTeam(id: "redsox", name: "Red Sox"),
                "phillies": makeTeam(id: "phillies", name: "Phillies")
            ],
            stadiums: [
                "yankee-stadium": yankeeStadium,
                "fenway-park": fenwayPark,
                "citizens-bank": citizensBank
            ]
        )

        let result = planner.plan(request: request)

        guard case .success(let options) = result else {
            Issue.record("Expected success")
            return
        }

        // Verify each route visits all 3 stadiums
        for option in options {
            let citiesVisited = Set(option.stops.map { $0.city })

            #expect(citiesVisited.contains("New York"), "Every route must visit New York")
            #expect(citiesVisited.contains("Boston"), "Every route must visit Boston")
            #expect(citiesVisited.contains("Philadelphia"), "Every route must visit Philadelphia")
        }
    }

    @Test("Integration: total duration within 6 days (teams x 2)")
    func integration_totalDurationWithinLimit() {
        let baseDate = Date()

        let yankeeStadium = makeStadium(
            id: "yankee-stadium",
            name: "Yankee Stadium",
            city: "New York",
            coordinate: yankeeStadiumCoord
        )
        let fenwayPark = makeStadium(
            id: "fenway-park",
            name: "Fenway Park",
            city: "Boston",
            coordinate: fenwayParkCoord
        )
        let citizensBank = makeStadium(
            id: "citizens-bank",
            name: "Citizens Bank Park",
            city: "Philadelphia",
            coordinate: citizensBankCoord
        )

        // Create games that fit within a 6-day window
        // For 3 teams, window = 6 days. Games must span at least 6 days.
        let calendar = Calendar.current
        let day1 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 1))!
        let day3 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 3))!
        let day6 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 6))!

        let yankeesGame = Game(
            id: "yankees-home",
            homeTeamId: "yankees",
            awayTeamId: "opponent",
            stadiumId: "yankee-stadium",
            dateTime: day1,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        let redsoxGame = Game(
            id: "redsox-home",
            homeTeamId: "redsox",
            awayTeamId: "opponent",
            stadiumId: "fenway-park",
            dateTime: day3,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        let philliesGame = Game(
            id: "phillies-home",
            homeTeamId: "phillies",
            awayTeamId: "opponent",
            stadiumId: "citizens-bank",
            dateTime: day6,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        let prefs = TripPreferences(
            planningMode: .teamFirst,
            sports: [.mlb],
            startDate: baseDate,
            endDate: baseDate.addingTimeInterval(86400 * 30),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 2,
            selectedTeamIds: ["yankees", "redsox", "phillies"]
        )

        // 3 teams * 2 = 6 days max window
        let maxDays = prefs.teamFirstMaxDays
        #expect(maxDays == 6, "Window should be 6 days for 3 teams")

        let request = PlanningRequest(
            preferences: prefs,
            availableGames: [yankeesGame, redsoxGame, philliesGame],
            teams: [
                "yankees": makeTeam(id: "yankees", name: "Yankees"),
                "redsox": makeTeam(id: "redsox", name: "Red Sox"),
                "phillies": makeTeam(id: "phillies", name: "Phillies")
            ],
            stadiums: [
                "yankee-stadium": yankeeStadium,
                "fenway-park": fenwayPark,
                "citizens-bank": citizensBank
            ]
        )

        let result = planner.plan(request: request)

        guard case .success(let options) = result else {
            Issue.record("Expected success")
            return
        }

        // Verify each route's duration is reasonable
        for option in options {
            guard let firstStop = option.stops.first,
                  let lastStop = option.stops.last else {
                continue
            }

            let calendar = Calendar.current
            let tripDays = calendar.dateComponents(
                [.day],
                from: calendar.startOfDay(for: firstStop.arrivalDate),
                to: calendar.startOfDay(for: lastStop.departureDate)
            ).day ?? 0

            // Trip duration should be within the window (allowing +1 for same-day start/end)
            #expect(tripDays <= maxDays, "Trip duration (\(tripDays) days) should not exceed window (\(maxDays) days)")
        }
    }

    @Test("Integration: factory selects ScenarioEPlanner for teamFirst mode")
    func integration_factorySelectsScenarioEPlanner() {
        let baseDate = Date()

        let prefs = TripPreferences(
            planningMode: .teamFirst,
            sports: [.mlb],
            startDate: baseDate,
            endDate: baseDate.addingTimeInterval(86400 * 30),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 2,
            selectedTeamIds: ["team1", "team2"]
        )

        let request = PlanningRequest(
            preferences: prefs,
            availableGames: [],
            teams: [:],
            stadiums: [:]
        )

        let scenario = ScenarioPlannerFactory.classify(request)
        #expect(scenario == .scenarioE, "Should classify as scenarioE for teamFirst mode with 2+ teams")

        let planner = ScenarioPlannerFactory.planner(for: request)
        #expect(planner is ScenarioEPlanner, "Should return ScenarioEPlanner for teamFirst mode")
    }

    @Test("Integration: factory requires 2+ teams for ScenarioE")
    func integration_factoryRequires2TeamsForScenarioE() {
        let baseDate = Date()

        // With only 1 team, should NOT select ScenarioE
        var prefs = TripPreferences(
            planningMode: .teamFirst,
            sports: [.mlb],
            startDate: baseDate,
            endDate: baseDate.addingTimeInterval(86400 * 30),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 2,
            selectedTeamIds: ["team1"]  // Only 1 team
        )

        var request = PlanningRequest(
            preferences: prefs,
            availableGames: [],
            teams: [:],
            stadiums: [:]
        )

        var scenario = ScenarioPlannerFactory.classify(request)
        #expect(scenario != .scenarioE, "Should NOT classify as scenarioE with only 1 team")

        // With 0 teams, should also NOT select ScenarioE
        prefs.selectedTeamIds = []
        request = PlanningRequest(
            preferences: prefs,
            availableGames: [],
            teams: [:],
            stadiums: [:]
        )

        scenario = ScenarioPlannerFactory.classify(request)
        #expect(scenario != .scenarioE, "Should NOT classify as scenarioE with 0 teams")
    }

    @Test("Integration: realistic east coast trip with 4 teams")
    func integration_realisticEastCoastTrip() {
        let baseDate = Date()

        // East coast stadiums (NYC, Boston, Philly, Baltimore)
        let yankeeStadium = makeStadium(
            id: "yankee-stadium",
            name: "Yankee Stadium",
            city: "New York",
            coordinate: CLLocationCoordinate2D(latitude: 40.8296, longitude: -73.9262)
        )
        let fenwayPark = makeStadium(
            id: "fenway-park",
            name: "Fenway Park",
            city: "Boston",
            coordinate: CLLocationCoordinate2D(latitude: 42.3467, longitude: -71.0972)
        )
        let citizensBank = makeStadium(
            id: "citizens-bank",
            name: "Citizens Bank Park",
            city: "Philadelphia",
            coordinate: CLLocationCoordinate2D(latitude: 39.9061, longitude: -75.1665)
        )
        let camdenYards = makeStadium(
            id: "camden-yards",
            name: "Camden Yards",
            city: "Baltimore",
            coordinate: CLLocationCoordinate2D(latitude: 39.2838, longitude: -76.6215)
        )

        // Create games spread across 8-day window (4 teams * 2 = 8 days)
        // For 4 teams, window = 8 days. Games must span at least 8 days.
        let calendar = Calendar.current
        let day1 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 1))!
        let day3 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 3))!
        let day5 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 5))!
        let day8 = calendar.date(bySettingHour: 19, minute: 0, second: 0, of: baseDate.addingTimeInterval(86400 * 8))!

        let games = [
            Game(id: "yankees-1", homeTeamId: "yankees", awayTeamId: "opp", stadiumId: "yankee-stadium",
                 dateTime: day1, sport: .mlb, season: "2026", isPlayoff: false),
            Game(id: "redsox-1", homeTeamId: "redsox", awayTeamId: "opp", stadiumId: "fenway-park",
                 dateTime: day3, sport: .mlb, season: "2026", isPlayoff: false),
            Game(id: "phillies-1", homeTeamId: "phillies", awayTeamId: "opp", stadiumId: "citizens-bank",
                 dateTime: day5, sport: .mlb, season: "2026", isPlayoff: false),
            Game(id: "orioles-1", homeTeamId: "orioles", awayTeamId: "opp", stadiumId: "camden-yards",
                 dateTime: day8, sport: .mlb, season: "2026", isPlayoff: false)
        ]

        let prefs = TripPreferences(
            planningMode: .teamFirst,
            sports: [.mlb],
            startDate: baseDate,
            endDate: baseDate.addingTimeInterval(86400 * 30),
            leisureLevel: .moderate,
            lodgingType: .hotel,
            numberOfDrivers: 2,
            selectedTeamIds: ["yankees", "redsox", "phillies", "orioles"]
        )

        let request = PlanningRequest(
            preferences: prefs,
            availableGames: games,
            teams: [
                "yankees": makeTeam(id: "yankees", name: "Yankees"),
                "redsox": makeTeam(id: "redsox", name: "Red Sox"),
                "phillies": makeTeam(id: "phillies", name: "Phillies"),
                "orioles": makeTeam(id: "orioles", name: "Orioles")
            ],
            stadiums: [
                "yankee-stadium": yankeeStadium,
                "fenway-park": fenwayPark,
                "citizens-bank": citizensBank,
                "camden-yards": camdenYards
            ]
        )

        let result = planner.plan(request: request)

        guard case .success(let options) = result else {
            Issue.record("Expected success with drivable east coast trip")
            return
        }

        #expect(!options.isEmpty, "Should find routes for east coast trip")

        // Verify each route visits all 4 cities
        for option in options {
            let citiesVisited = Set(option.stops.map { $0.city })
            #expect(citiesVisited.count >= 4, "Each route should visit at least 4 cities (one per team)")
        }
    }

    // MARK: - Helper Methods

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

    private func makeTeam(id: String, name: String) -> Team {
        Team(
            id: id,
            name: name,
            abbreviation: String(name.prefix(3)).uppercased(),
            sport: .mlb,
            city: "Test City",
            stadiumId: id
        )
    }
}
