//
//  TripStopTests.swift
//  SportsTimeTests
//
//  TDD specification tests for TripStop model.
//

import Testing
import Foundation
@testable import SportsTime

@Suite("TripStop")
struct TripStopTests {

    // MARK: - Test Data

    private func makeStop(
        arrivalDate: Date,
        departureDate: Date,
        games: [String] = []
    ) -> TripStop {
        TripStop(
            stopNumber: 1,
            city: "New York",
            state: "NY",
            arrivalDate: arrivalDate,
            departureDate: departureDate,
            games: games
        )
    }

    // MARK: - Specification Tests: stayDuration

    @Test("stayDuration: same day arrival and departure returns 1")
    func stayDuration_sameDay() {
        let calendar = Calendar.current
        let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
        let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!

        let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)

        #expect(stop.stayDuration == 1)
    }

    @Test("stayDuration: 2-day stay returns 2")
    func stayDuration_twoDays() {
        let calendar = Calendar.current
        let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
        let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 16))!

        let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)

        #expect(stop.stayDuration == 1, "1 day between 15th and 16th")
    }

    @Test("stayDuration: week-long stay")
    func stayDuration_weekLong() {
        let calendar = Calendar.current
        let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
        let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 22))!

        let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)

        #expect(stop.stayDuration == 7, "7 days between 15th and 22nd")
    }

    @Test("stayDuration: minimum is 1 even if dates are reversed")
    func stayDuration_minimumIsOne() {
        let calendar = Calendar.current
        let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 20))!
        let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!

        let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)

        #expect(stop.stayDuration >= 1, "stayDuration should never be less than 1")
    }

    // MARK: - Specification Tests: hasGames

    @Test("hasGames: true when games array is non-empty")
    func hasGames_true() {
        let now = Date()
        let stop = makeStop(arrivalDate: now, departureDate: now, games: ["game1", "game2"])

        #expect(stop.hasGames == true)
    }

    @Test("hasGames: false when games array is empty")
    func hasGames_false() {
        let now = Date()
        let stop = makeStop(arrivalDate: now, departureDate: now, games: [])

        #expect(stop.hasGames == false)
    }

    // MARK: - Specification Tests: formattedDateRange

    @Test("formattedDateRange: single date for 1-day stay")
    func formattedDateRange_singleDay() {
        let calendar = Calendar.current
        let date = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!

        let stop = makeStop(arrivalDate: date, departureDate: date)

        // Should show just "Jun 15"
        #expect(stop.formattedDateRange == "Jun 15")
    }

    @Test("formattedDateRange: range for multi-day stay")
    func formattedDateRange_multiDay() {
        let calendar = Calendar.current
        let arrival = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
        let departure = calendar.date(from: DateComponents(year: 2026, month: 6, day: 18))!

        let stop = makeStop(arrivalDate: arrival, departureDate: departure)

        // Should show "Jun 15 - Jun 18"
        #expect(stop.formattedDateRange == "Jun 15 - Jun 18")
    }

    // MARK: - Specification Tests: locationDescription

    @Test("locationDescription: combines city and state")
    func locationDescription_format() {
        let stop = TripStop(
            stopNumber: 1,
            city: "Boston",
            state: "MA",
            arrivalDate: Date(),
            departureDate: Date()
        )

        #expect(stop.locationDescription == "Boston, MA")
    }

    // MARK: - Invariant Tests

    @Test("Invariant: stayDuration >= 1")
    func invariant_stayDurationAtLeastOne() {
        let calendar = Calendar.current

        // Test various date combinations
        let testCases: [(arrival: DateComponents, departure: DateComponents)] = [
            (DateComponents(year: 2026, month: 6, day: 15), DateComponents(year: 2026, month: 6, day: 15)),
            (DateComponents(year: 2026, month: 6, day: 15), DateComponents(year: 2026, month: 6, day: 16)),
            (DateComponents(year: 2026, month: 6, day: 15), DateComponents(year: 2026, month: 6, day: 22)),
            (DateComponents(year: 2026, month: 12, day: 31), DateComponents(year: 2027, month: 1, day: 2)),
        ]

        for (arrival, departure) in testCases {
            let arrivalDate = calendar.date(from: arrival)!
            let departureDate = calendar.date(from: departure)!
            let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)

            #expect(stop.stayDuration >= 1, "stayDuration should be at least 1")
        }
    }

    @Test("Invariant: hasGames equals !games.isEmpty")
    func invariant_hasGamesConsistent() {
        let now = Date()

        let stopWithGames = makeStop(arrivalDate: now, departureDate: now, games: ["game1"])
        #expect(stopWithGames.hasGames == !stopWithGames.games.isEmpty)

        let stopWithoutGames = makeStop(arrivalDate: now, departureDate: now, games: [])
        #expect(stopWithoutGames.hasGames == !stopWithoutGames.games.isEmpty)
    }

    // MARK: - Property Tests

    @Test("Property: isRestDay defaults to false")
    func property_isRestDayDefault() {
        let now = Date()
        let stop = makeStop(arrivalDate: now, departureDate: now)

        #expect(stop.isRestDay == false)
    }

    @Test("Property: isRestDay can be set to true")
    func property_isRestDayTrue() {
        let stop = TripStop(
            stopNumber: 1,
            city: "City",
            state: "ST",
            arrivalDate: Date(),
            departureDate: Date(),
            isRestDay: true
        )

        #expect(stop.isRestDay == true)
    }

    @Test("Property: optional fields can be nil")
    func property_optionalFieldsNil() {
        let stop = TripStop(
            stopNumber: 1,
            city: "City",
            state: "ST",
            coordinate: nil,
            arrivalDate: Date(),
            departureDate: Date(),
            stadium: nil,
            lodging: nil,
            notes: nil
        )

        #expect(stop.coordinate == nil)
        #expect(stop.stadium == nil)
        #expect(stop.lodging == nil)
        #expect(stop.notes == nil)
    }
}
