//
//  StadiumTests.swift
//  SportsTimeTests
//
//  TDD specification tests for Stadium model.
//

import Testing
import CoreLocation
@testable import SportsTime

@Suite("Stadium")
struct StadiumTests {

    // MARK: - Test Data

    private let nycCoord = CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855)
    private let bostonCoord = CLLocationCoordinate2D(latitude: 42.3467, longitude: -71.0972)
    private let chicagoCoord = CLLocationCoordinate2D(latitude: 41.8827, longitude: -87.6233)
    private let laCoord = CLLocationCoordinate2D(latitude: 34.0430, longitude: -118.2673)

    private func makeStadium(
        id: String = "stadium1",
        latitude: Double,
        longitude: Double
    ) -> Stadium {
        Stadium(
            id: id,
            name: "Test Stadium",
            city: "Test City",
            state: "XX",
            latitude: latitude,
            longitude: longitude,
            capacity: 40000,
            sport: .mlb
        )
    }

    // MARK: - Specification Tests: region

    @Test("region: NYC longitude returns .east")
    func region_nycEast() {
        let stadium = makeStadium(latitude: nycCoord.latitude, longitude: nycCoord.longitude)
        #expect(stadium.region == .east)
    }

    @Test("region: Boston longitude returns .east")
    func region_bostonEast() {
        let stadium = makeStadium(latitude: bostonCoord.latitude, longitude: bostonCoord.longitude)
        #expect(stadium.region == .east)
    }

    @Test("region: Chicago longitude returns .central")
    func region_chicagoCentral() {
        let stadium = makeStadium(latitude: chicagoCoord.latitude, longitude: chicagoCoord.longitude)
        #expect(stadium.region == .central)
    }

    @Test("region: LA longitude returns .west")
    func region_laWest() {
        let stadium = makeStadium(latitude: laCoord.latitude, longitude: laCoord.longitude)
        #expect(stadium.region == .west)
    }

    // MARK: - Specification Tests: coordinate

    @Test("coordinate returns CLLocationCoordinate2D from lat/long")
    func coordinate_returnsCorrectValue() {
        let stadium = makeStadium(latitude: 40.7580, longitude: -73.9855)

        #expect(stadium.coordinate.latitude == 40.7580)
        #expect(stadium.coordinate.longitude == -73.9855)
    }

    @Test("location returns CLLocation from lat/long")
    func location_returnsCorrectValue() {
        let stadium = makeStadium(latitude: 40.7580, longitude: -73.9855)

        #expect(stadium.location.coordinate.latitude == 40.7580)
        #expect(stadium.location.coordinate.longitude == -73.9855)
    }

    // MARK: - Specification Tests: distance

    @Test("distance between NYC and Boston stadiums")
    func distance_nycToBoston() {
        let nycStadium = makeStadium(id: "nyc", latitude: nycCoord.latitude, longitude: nycCoord.longitude)
        let bostonStadium = makeStadium(id: "boston", latitude: bostonCoord.latitude, longitude: bostonCoord.longitude)

        let distance = nycStadium.distance(to: bostonStadium)

        // NYC to Boston is approximately 298-300 km / ~186 miles with these coordinates
        #expect(distance > 295_000, "NYC to Boston should be > 295km")
        #expect(distance < 310_000, "NYC to Boston should be < 310km")
    }

    @Test("distance to self is zero")
    func distance_toSelf() {
        let stadium = makeStadium(latitude: nycCoord.latitude, longitude: nycCoord.longitude)
        let distance = stadium.distance(to: stadium)

        #expect(distance == 0)
    }

    @Test("distance from coordinate")
    func distance_fromCoordinate() {
        let stadium = makeStadium(latitude: nycCoord.latitude, longitude: nycCoord.longitude)
        let distance = stadium.distance(from: bostonCoord)

        // Same as NYC to Boston (~298-300 km depending on exact coordinates)
        #expect(distance > 295_000)
        #expect(distance < 310_000)
    }

    // MARK: - Specification Tests: Equality

    @Test("equality based on id only")
    func equality_basedOnId() {
        let stadium1 = Stadium(
            id: "stadium1",
            name: "Stadium A",
            city: "City A",
            state: "AA",
            latitude: 40.0,
            longitude: -70.0,
            capacity: 40000,
            sport: .mlb
        )

        let stadium2 = Stadium(
            id: "stadium1",
            name: "Different Name",
            city: "Different City",
            state: "BB",
            latitude: 30.0,
            longitude: -80.0,
            capacity: 50000,
            sport: .nba
        )

        #expect(stadium1 == stadium2, "Stadiums with same id should be equal")
    }

    @Test("inequality when ids differ")
    func inequality_differentIds() {
        let stadium1 = makeStadium(id: "stadium1", latitude: 40.0, longitude: -70.0)
        let stadium2 = makeStadium(id: "stadium2", latitude: 40.0, longitude: -70.0)

        #expect(stadium1 != stadium2, "Stadiums with different ids should not be equal")
    }

    // MARK: - Invariant Tests

    @Test("Invariant: region is consistent with Region.classify")
    func invariant_regionConsistentWithClassify() {
        let testCases: [(lat: Double, lon: Double)] = [
            (40.7580, -73.9855),  // NYC
            (41.8827, -87.6233),  // Chicago
            (34.0430, -118.2673), // LA
            (42.3467, -71.0972),  // Boston
            (33.4484, -112.0740), // Phoenix
        ]

        for (lat, lon) in testCases {
            let stadium = makeStadium(latitude: lat, longitude: lon)
            let expected = Region.classify(longitude: lon)
            #expect(stadium.region == expected, "Stadium at \(lon) should be in \(expected)")
        }
    }

    @Test("Invariant: location and coordinate match lat/long")
    func invariant_locationAndCoordinateMatch() {
        let stadium = makeStadium(latitude: 40.7580, longitude: -73.9855)

        #expect(stadium.location.coordinate.latitude == stadium.latitude)
        #expect(stadium.location.coordinate.longitude == stadium.longitude)
        #expect(stadium.coordinate.latitude == stadium.latitude)
        #expect(stadium.coordinate.longitude == stadium.longitude)
    }

    // MARK: - Property Tests

    @Test("Property: fullAddress combines city and state")
    func property_fullAddress() {
        let stadium = Stadium(
            id: "test",
            name: "Test Stadium",
            city: "New York",
            state: "NY",
            latitude: 40.0,
            longitude: -74.0,
            capacity: 40000,
            sport: .mlb
        )

        #expect(stadium.fullAddress == "New York, NY")
    }

    @Test("Property: timeZone returns nil when identifier is nil")
    func property_timeZoneNil() {
        let stadium = Stadium(
            id: "test",
            name: "Test Stadium",
            city: "Test",
            state: "XX",
            latitude: 40.0,
            longitude: -74.0,
            capacity: 40000,
            sport: .mlb,
            timeZoneIdentifier: nil
        )

        #expect(stadium.timeZone == nil)
    }

    @Test("Property: timeZone returns TimeZone when identifier is valid")
    func property_timeZoneValid() {
        let stadium = Stadium(
            id: "test",
            name: "Test Stadium",
            city: "Test",
            state: "XX",
            latitude: 40.0,
            longitude: -74.0,
            capacity: 40000,
            sport: .mlb,
            timeZoneIdentifier: "America/New_York"
        )

        #expect(stadium.timeZone?.identifier == "America/New_York")
    }
}
