//
//  GameTests.swift
//  SportsTimeTests
//
//  TDD specification tests for Game model.
//

import Testing
import Foundation
@testable import SportsTime

@Suite("Game")
@MainActor
struct GameTests {

    // MARK: - Test Data

    private func makeGame(dateTime: Date) -> Game {
        Game(
            id: "game1",
            homeTeamId: "team1",
            awayTeamId: "team2",
            stadiumId: "stadium1",
            dateTime: dateTime,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )
    }

    // MARK: - Specification Tests: gameDate

    @Test("gameDate returns start of day for dateTime")
    func gameDate_returnsStartOfDay() {
        let calendar = Calendar.current

        // Game at 7:05 PM
        let dateTime = calendar.date(from: DateComponents(
            year: 2026, month: 6, day: 15,
            hour: 19, minute: 5, second: 0
        ))!

        let game = makeGame(dateTime: dateTime)

        let expectedStart = calendar.startOfDay(for: dateTime)
        #expect(game.gameDate == expectedStart)

        // Verify it's at midnight
        let components = calendar.dateComponents([.hour, .minute, .second], from: game.gameDate)
        #expect(components.hour == 0)
        #expect(components.minute == 0)
        #expect(components.second == 0)
    }

    @Test("gameDate is same for games on same calendar day")
    func gameDate_sameDay() {
        let calendar = Calendar.current

        // Morning game
        let morningTime = calendar.date(from: DateComponents(
            year: 2026, month: 6, day: 15,
            hour: 10, minute: 0
        ))!

        // Evening game
        let eveningTime = calendar.date(from: DateComponents(
            year: 2026, month: 6, day: 15,
            hour: 20, minute: 0
        ))!

        let morningGame = makeGame(dateTime: morningTime)
        let eveningGame = makeGame(dateTime: eveningTime)

        #expect(morningGame.gameDate == eveningGame.gameDate)
    }

    @Test("gameDate differs for games on different calendar days")
    func gameDate_differentDays() {
        let calendar = Calendar.current

        let day1 = calendar.date(from: DateComponents(
            year: 2026, month: 6, day: 15, hour: 19
        ))!
        let day2 = calendar.date(from: DateComponents(
            year: 2026, month: 6, day: 16, hour: 19
        ))!

        let game1 = makeGame(dateTime: day1)
        let game2 = makeGame(dateTime: day2)

        #expect(game1.gameDate != game2.gameDate)
    }

    // MARK: - Specification Tests: startTime Alias

    @Test("startTime is alias for dateTime")
    func startTime_isAliasForDateTime() {
        let dateTime = Date()
        let game = makeGame(dateTime: dateTime)

        #expect(game.startTime == game.dateTime)
    }

    // MARK: - Specification Tests: Equality

    @Test("equality based on id only")
    func equality_basedOnId() {
        let dateTime = Date()

        let game1 = Game(
            id: "game1",
            homeTeamId: "team1",
            awayTeamId: "team2",
            stadiumId: "stadium1",
            dateTime: dateTime,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        // Same id, different fields
        let game2 = Game(
            id: "game1",
            homeTeamId: "different-team",
            awayTeamId: "different-team2",
            stadiumId: "different-stadium",
            dateTime: dateTime.addingTimeInterval(3600),
            sport: .nba,
            season: "2027",
            isPlayoff: true
        )

        #expect(game1 == game2, "Games with same id should be equal")
    }

    @Test("inequality when ids differ")
    func inequality_differentIds() {
        let dateTime = Date()

        let game1 = Game(
            id: "game1",
            homeTeamId: "team1",
            awayTeamId: "team2",
            stadiumId: "stadium1",
            dateTime: dateTime,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        let game2 = Game(
            id: "game2",
            homeTeamId: "team1",
            awayTeamId: "team2",
            stadiumId: "stadium1",
            dateTime: dateTime,
            sport: .mlb,
            season: "2026",
            isPlayoff: false
        )

        #expect(game1 != game2, "Games with different ids should not be equal")
    }

    // MARK: - Invariant Tests

    @Test("Invariant: gameDate is always at midnight")
    func invariant_gameDateAtMidnight() {
        let calendar = Calendar.current

        // Test various times throughout the day
        let times = [0, 6, 12, 18, 23].map { hour in
            calendar.date(from: DateComponents(year: 2026, month: 6, day: 15, hour: hour))!
        }

        for time in times {
            let game = makeGame(dateTime: time)
            let components = calendar.dateComponents([.hour, .minute, .second], from: game.gameDate)
            #expect(components.hour == 0, "gameDate hour should be 0")
            #expect(components.minute == 0, "gameDate minute should be 0")
            #expect(components.second == 0, "gameDate second should be 0")
        }
    }

    @Test("Invariant: startTime equals dateTime")
    func invariant_startTimeEqualsDateTime() {
        for _ in 0..<10 {
            let dateTime = Date().addingTimeInterval(Double.random(in: -86400...86400))
            let game = makeGame(dateTime: dateTime)
            #expect(game.startTime == game.dateTime)
        }
    }

    // MARK: - Property Tests

    @Test("Property: gameDate is in same calendar day as dateTime")
    func property_gameDateSameCalendarDay() {
        let calendar = Calendar.current

        let dateTime = calendar.date(from: DateComponents(
            year: 2026, month: 7, day: 4, hour: 19, minute: 5
        ))!

        let game = makeGame(dateTime: dateTime)

        let dateTimeDay = calendar.component(.day, from: dateTime)
        let gameDateDay = calendar.component(.day, from: game.gameDate)

        #expect(dateTimeDay == gameDateDay)
    }
}
