//
//  TripPreferences.swift
//  SportsTime
//
//  User preferences for trip planning.
//
//  - Expected Behavior:
//    - totalDriverHoursPerDay = (maxDrivingHoursPerDriver ?? 8.0) * numberOfDrivers
//    - effectiveTripDuration uses tripDuration if set, otherwise calculates from date range
//    - effectiveTripDuration is at least 1 day
//    - LeisureLevel.restDaysPerWeek: packed=0.5, moderate=1.5, relaxed=2.5
//    - LeisureLevel.maxGamesPerWeek: packed=7, moderate=5, relaxed=3
//    - RoutePreference.scenicWeight: direct=0.0, scenic=1.0, balanced=0.5
//
//  - Invariants:
//    - totalDriverHoursPerDay > 0 (numberOfDrivers >= 1)
//    - effectiveTripDuration >= 1
//    - LocationInput.isResolved == (coordinate != nil)
//

import Foundation
import CoreLocation

// MARK: - Planning Mode

enum PlanningMode: String, Codable, CaseIterable, Identifiable {
    case dateRange      // Start date + end date, find games in range
    case gameFirst      // Pick games first, trip around those games
    case locations      // Start/end locations, optional games along route
    case followTeam     // Follow one team's schedule (home + away)
    case teamFirst      // Select teams, find optimal trip windows across season

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .dateRange: return "By Dates"
        case .gameFirst: return "By Games"
        case .locations: return "By Route"
        case .followTeam: return "Follow Team"
        case .teamFirst: return "By Teams"
        }
    }

    var description: String {
        switch self {
        case .dateRange: return "Shows a curated sample of possible routes — use filters to find your ideal trip"
        case .gameFirst: return "Build trip around specific games"
        case .locations: return "Plan route between locations"
        case .followTeam: return "Follow your team on the road"
        case .teamFirst: return "Select teams, find best trip windows"
        }
    }

    var iconName: String {
        switch self {
        case .dateRange: return "calendar"
        case .gameFirst: return "sportscourt"
        case .locations: return "map"
        case .followTeam: return "person.3.fill"
        case .teamFirst: return "person.2.badge.gearshape"
        }
    }
}

// MARK: - Travel Mode

enum TravelMode: String, Codable, CaseIterable, Identifiable {
    case drive
    case fly

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .drive: return "Drive"
        case .fly: return "Fly"
        }
    }

    var iconName: String {
        switch self {
        case .drive: return "car.fill"
        case .fly: return "airplane"
        }
    }
}

// MARK: - Lodging Type

enum LodgingType: String, Codable, CaseIterable, Identifiable {
    case hotel
    case camperRV
    case airbnb
    case flexible

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .hotel: return "Hotels"
        case .camperRV: return "Camper / RV"
        case .airbnb: return "Airbnb"
        case .flexible: return "Flexible"
        }
    }

    var iconName: String {
        switch self {
        case .hotel: return "building.2.fill"
        case .camperRV: return "bus.fill"
        case .airbnb: return "house.fill"
        case .flexible: return "questionmark.circle.fill"
        }
    }
}

// MARK: - Leisure Level

enum LeisureLevel: String, Codable, CaseIterable, Identifiable {
    case packed
    case moderate
    case relaxed

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .packed: return "Packed"
        case .moderate: return "Moderate"
        case .relaxed: return "Relaxed"
        }
    }

    var description: String {
        switch self {
        case .packed: return "Maximize games, minimal downtime"
        case .moderate: return "Balance games with rest days"
        case .relaxed: return "Prioritize comfort, fewer games"
        }
    }

    var restDaysPerWeek: Double {
        switch self {
        case .packed: return 0.5
        case .moderate: return 1.5
        case .relaxed: return 2.5
        }
    }

    var maxGamesPerWeek: Int {
        switch self {
        case .packed: return 7
        case .moderate: return 5
        case .relaxed: return 3
        }
    }
}

// MARK: - Route Preference

enum RoutePreference: String, Codable, CaseIterable, Identifiable {
    case direct
    case scenic
    case balanced

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .direct: return "Direct"
        case .scenic: return "Scenic"
        case .balanced: return "Balanced"
        }
    }

    var scenicWeight: Double {
        switch self {
        case .direct: return 0.0
        case .scenic: return 1.0
        case .balanced: return 0.5
        }
    }
}

// MARK: - Location Input

struct LocationInput: Codable, Hashable {
    let name: String
    let coordinate: CLLocationCoordinate2D?
    let address: String?

    init(name: String, coordinate: CLLocationCoordinate2D? = nil, address: String? = nil) {
        self.name = name
        self.coordinate = coordinate
        self.address = address
    }

    var isResolved: Bool { coordinate != nil }
}

extension CLLocationCoordinate2D: @retroactive Codable, @retroactive Hashable, @retroactive Equatable {
    enum CodingKeys: String, CodingKey {
        case latitude, longitude
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let lat = try container.decode(Double.self, forKey: .latitude)
        let lon = try container.decode(Double.self, forKey: .longitude)
        self.init(latitude: lat, longitude: lon)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(latitude, forKey: .latitude)
        try container.encode(longitude, forKey: .longitude)
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(latitude)
        hasher.combine(longitude)
    }

    public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
        lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
    }
}

// MARK: - Trip Preferences

struct TripPreferences: Codable, Hashable {
    var planningMode: PlanningMode
    var startLocation: LocationInput?
    var endLocation: LocationInput?
    var sports: Set<Sport>
    var mustSeeGameIds: Set<String>
    var travelMode: TravelMode
    var startDate: Date
    var endDate: Date

    var numberOfStops: Int?
    var tripDuration: Int?
    var leisureLevel: LeisureLevel

    var mustStopLocations: [LocationInput]
    var preferredCities: [String]
    var routePreference: RoutePreference
    var needsEVCharging: Bool
    var lodgingType: LodgingType
    var numberOfDrivers: Int
    var maxDrivingHoursPerDriver: Double?
    var allowRepeatCities: Bool
    var selectedRegions: Set<Region>

    /// Team to follow (for Follow Team mode)
    var followTeamId: String?

    /// Whether to start/end from a home location (vs fly-in/fly-out)
    var useHomeLocation: Bool

    /// Trip duration for gameFirst mode sliding windows (number of days)
    var gameFirstTripDuration: Int

    /// Teams to visit (for Team-First mode) - canonical team IDs
    var selectedTeamIds: Set<String> = []

    init(
        planningMode: PlanningMode = .dateRange,
        startLocation: LocationInput? = nil,
        endLocation: LocationInput? = nil,
        sports: Set<Sport> = [],
        mustSeeGameIds: Set<String> = [],
        travelMode: TravelMode = .drive,
        startDate: Date = Date(),
        endDate: Date = Date().addingTimeInterval(86400 * 7),
        numberOfStops: Int? = nil,
        tripDuration: Int? = nil,
        leisureLevel: LeisureLevel = .moderate,
        mustStopLocations: [LocationInput] = [],
        preferredCities: [String] = [],
        routePreference: RoutePreference = .balanced,
        needsEVCharging: Bool = false,
        lodgingType: LodgingType = .hotel,
        numberOfDrivers: Int = 1,
        maxDrivingHoursPerDriver: Double? = nil,
        allowRepeatCities: Bool = true,
        selectedRegions: Set<Region> = [.east, .central, .west],
        followTeamId: String? = nil,
        useHomeLocation: Bool = true,
        gameFirstTripDuration: Int = 7,
        selectedTeamIds: Set<String> = []
    ) {
        self.planningMode = planningMode
        self.startLocation = startLocation
        self.endLocation = endLocation
        self.sports = sports
        self.mustSeeGameIds = mustSeeGameIds
        self.travelMode = travelMode
        self.startDate = startDate
        self.endDate = endDate
        self.numberOfStops = numberOfStops
        self.tripDuration = tripDuration
        self.leisureLevel = leisureLevel
        self.mustStopLocations = mustStopLocations
        self.preferredCities = preferredCities
        self.routePreference = routePreference
        self.needsEVCharging = needsEVCharging
        self.lodgingType = lodgingType
        self.numberOfDrivers = numberOfDrivers
        self.maxDrivingHoursPerDriver = maxDrivingHoursPerDriver
        self.allowRepeatCities = allowRepeatCities
        self.selectedRegions = selectedRegions
        self.followTeamId = followTeamId
        self.useHomeLocation = useHomeLocation
        self.gameFirstTripDuration = gameFirstTripDuration
        self.selectedTeamIds = selectedTeamIds
    }

    var totalDriverHoursPerDay: Double {
        let maxPerDriver = maxDrivingHoursPerDriver ?? 8.0
        return maxPerDriver * Double(numberOfDrivers)
    }

    var effectiveTripDuration: Int {
        if let duration = tripDuration { return duration }
        let days = Calendar.current.dateComponents([.day], from: startDate, to: endDate).day ?? 7
        return max(1, days)
    }

    /// Maximum trip duration for Team-First mode (2 days per selected team)
    var teamFirstMaxDays: Int {
        selectedTeamIds.count * 2
    }
}
