import Foundation

/// Validates itinerary item positions and calculates valid drop zones
struct ItineraryConstraints {
    let tripDayCount: Int
    private let items: [ItineraryItem]

    init(tripDayCount: Int, items: [ItineraryItem]) {
        self.tripDayCount = tripDayCount
        self.items = items
    }

    // MARK: - Public API

    /// Check if a position is valid for an item
    func isValidPosition(for item: ItineraryItem, day: Int, sortOrder: Double) -> Bool {
        // Day must be within trip range
        guard day >= 1 && day <= tripDayCount else { return false }

        switch item.kind {
        case .game:
            // Games are fixed, should never be moved
            return false

        case .travel(let info):
            return isValidTravelPosition(
                fromCity: info.fromCity,
                toCity: info.toCity,
                day: day,
                sortOrder: sortOrder
            )

        case .custom:
            // Custom items can go anywhere
            return true
        }
    }

    /// Get the valid day range for a travel item
    func validDayRange(for item: ItineraryItem) -> ClosedRange<Int>? {
        guard case .travel(let info) = item.kind else { return nil }

        let departureGameDays = gameDays(in: info.fromCity)
        let arrivalGameDays = gameDays(in: info.toCity)

        // Can leave on or after the day of last departure game
        let minDay = departureGameDays.max() ?? 1
        // Must arrive on or before the day of first arrival game
        let maxDay = arrivalGameDays.min() ?? tripDayCount

        guard minDay <= maxDay else { return nil }
        return minDay...maxDay
    }

    /// Get the games that act as barriers for a travel item (for visual highlighting)
    func barrierGames(for item: ItineraryItem) -> [ItineraryItem] {
        guard case .travel(let info) = item.kind else { return [] }

        var barriers: [ItineraryItem] = []

        // Last game in departure city
        let departureGames = games(in: info.fromCity).sorted { $0.day < $1.day || ($0.day == $1.day && $0.sortOrder < $1.sortOrder) }
        if let lastDeparture = departureGames.last {
            barriers.append(lastDeparture)
        }

        // First game in arrival city
        let arrivalGames = games(in: info.toCity).sorted { $0.day < $1.day || ($0.day == $1.day && $0.sortOrder < $1.sortOrder) }
        if let firstArrival = arrivalGames.first {
            barriers.append(firstArrival)
        }

        return barriers
    }

    // MARK: - Private Helpers

    private func isValidTravelPosition(fromCity: String, toCity: String, day: Int, sortOrder: Double) -> Bool {
        let departureGames = games(in: fromCity)
        let arrivalGames = games(in: toCity)
        let departureGameDays = departureGames.map { $0.day }
        let arrivalGameDays = arrivalGames.map { $0.day }

        let minDay = departureGameDays.max() ?? 1
        let maxDay = arrivalGameDays.min() ?? tripDayCount

        // Check day is in valid range
        guard day >= minDay && day <= maxDay else { return false }

        // Check sortOrder constraints on edge days.
        // Must be strictly AFTER all departure games on that day
        if departureGameDays.contains(day) {
            let gamesOnDay = departureGames.filter { $0.day == day }
            let maxGameSortOrder = gamesOnDay.map { $0.sortOrder }.max() ?? 0
            if sortOrder <= maxGameSortOrder {
                return false
            }
        }

        // Must be strictly BEFORE all arrival games on that day
        if arrivalGameDays.contains(day) {
            let gamesOnDay = arrivalGames.filter { $0.day == day }
            let minGameSortOrder = gamesOnDay.map { $0.sortOrder }.min() ?? 0
            if sortOrder >= minGameSortOrder {
                return false
            }
        }

        return true
    }

    private func gameDays(in city: String) -> [Int] {
        return games(in: city).map { $0.day }
    }

    private func games(in city: String) -> [ItineraryItem] {
        return items.filter { item in
            guard let gameCity = item.gameCity else { return false }
            return gameCity.lowercased() == city.lowercased()
        }
    }
}
