//
//  DivisionTests.swift
//  SportsTimeTests
//
//  TDD specification tests for Division, Conference, and LeagueStructure models.
//

import Testing
@testable import SportsTime

@Suite("Division")
struct DivisionTests {

    // MARK: - Specification Tests: teamCount

    @Test("teamCount: equals teamCanonicalIds.count")
    func teamCount_equalsArrayCount() {
        let division = Division(
            id: "test_div",
            name: "Test Division",
            conference: "Test Conference",
            conferenceId: "test_conf",
            sport: .mlb,
            teamCanonicalIds: ["team1", "team2", "team3"]
        )

        #expect(division.teamCount == 3)
    }

    @Test("teamCount: is 0 for empty array")
    func teamCount_emptyArray() {
        let division = Division(
            id: "test_div",
            name: "Test Division",
            conference: "Test Conference",
            conferenceId: "test_conf",
            sport: .mlb,
            teamCanonicalIds: []
        )

        #expect(division.teamCount == 0)
    }

    // MARK: - Invariant Tests

    @Test("Invariant: teamCount == teamCanonicalIds.count")
    func invariant_teamCountMatchesArray() {
        let testCounts = [0, 1, 5, 10]

        for count in testCounts {
            let teamIds = (0..<count).map { "team_\($0)" }
            let division = Division(
                id: "div_\(count)",
                name: "Division \(count)",
                conference: "Conference",
                conferenceId: "conf",
                sport: .mlb,
                teamCanonicalIds: teamIds
            )

            #expect(division.teamCount == division.teamCanonicalIds.count)
        }
    }
}

// MARK: - Conference Tests

@Suite("Conference")
struct ConferenceTests {

    @Test("Property: divisionIds is accessible")
    func property_divisionIds() {
        let conference = Conference(
            id: "test_conf",
            name: "Test Conference",
            abbreviation: "TC",
            sport: .mlb,
            divisionIds: ["div1", "div2", "div3"]
        )

        #expect(conference.divisionIds.count == 3)
        #expect(conference.divisionIds.contains("div1"))
    }

    @Test("Property: abbreviation can be nil")
    func property_abbreviationNil() {
        let conference = Conference(
            id: "test_conf",
            name: "Test Conference",
            abbreviation: nil,
            sport: .nba,
            divisionIds: []
        )

        #expect(conference.abbreviation == nil)
    }
}

// MARK: - LeagueStructure Tests

@Suite("LeagueStructure")
struct LeagueStructureTests {

    // MARK: - Specification Tests: divisions(for:)

    @Test("divisions(for:): MLB returns 6 divisions")
    func divisions_mlb() {
        let divisions = LeagueStructure.divisions(for: .mlb)

        #expect(divisions.count == 6)
    }

    @Test("divisions(for:): NBA returns 6 divisions")
    func divisions_nba() {
        let divisions = LeagueStructure.divisions(for: .nba)

        #expect(divisions.count == 6)
    }

    @Test("divisions(for:): NHL returns 4 divisions")
    func divisions_nhl() {
        let divisions = LeagueStructure.divisions(for: .nhl)

        #expect(divisions.count == 4)
    }

    @Test("divisions(for:): MLS returns empty array")
    func divisions_mls() {
        let divisions = LeagueStructure.divisions(for: .mls)

        #expect(divisions.isEmpty)
    }

    @Test("divisions(for:): NFL returns empty array")
    func divisions_nfl() {
        let divisions = LeagueStructure.divisions(for: .nfl)

        #expect(divisions.isEmpty)
    }

    // MARK: - Specification Tests: conferences(for:)

    @Test("conferences(for:): MLB returns 2 conferences")
    func conferences_mlb() {
        let conferences = LeagueStructure.conferences(for: .mlb)

        #expect(conferences.count == 2)
    }

    @Test("conferences(for:): NBA returns 2 conferences")
    func conferences_nba() {
        let conferences = LeagueStructure.conferences(for: .nba)

        #expect(conferences.count == 2)
    }

    @Test("conferences(for:): NHL returns 2 conferences")
    func conferences_nhl() {
        let conferences = LeagueStructure.conferences(for: .nhl)

        #expect(conferences.count == 2)
    }

    @Test("conferences(for:): MLS returns empty array")
    func conferences_mls() {
        let conferences = LeagueStructure.conferences(for: .mls)

        #expect(conferences.isEmpty)
    }

    // MARK: - Specification Tests: division(byId:)

    @Test("division(byId:): finds MLB division")
    func divisionById_found() {
        let division = LeagueStructure.division(byId: "mlb_al_east")

        #expect(division != nil)
        #expect(division?.name == "AL East")
        #expect(division?.sport == .mlb)
    }

    @Test("division(byId:): finds NBA division")
    func divisionById_nba() {
        let division = LeagueStructure.division(byId: "nba_pacific")

        #expect(division != nil)
        #expect(division?.name == "Pacific")
        #expect(division?.sport == .nba)
    }

    @Test("division(byId:): finds NHL division")
    func divisionById_nhl() {
        let division = LeagueStructure.division(byId: "nhl_metropolitan")

        #expect(division != nil)
        #expect(division?.name == "Metropolitan")
        #expect(division?.sport == .nhl)
    }

    @Test("division(byId:): returns nil for unknown ID")
    func divisionById_notFound() {
        let division = LeagueStructure.division(byId: "unknown_division")

        #expect(division == nil)
    }

    // MARK: - Specification Tests: conference(byId:)

    @Test("conference(byId:): finds MLB conference")
    func conferenceById_found() {
        let conference = LeagueStructure.conference(byId: "mlb_nl")

        #expect(conference != nil)
        #expect(conference?.name == "National League")
        #expect(conference?.abbreviation == "NL")
    }

    @Test("conference(byId:): finds NBA conference")
    func conferenceById_nba() {
        let conference = LeagueStructure.conference(byId: "nba_western")

        #expect(conference != nil)
        #expect(conference?.name == "Western Conference")
    }

    @Test("conference(byId:): returns nil for unknown ID")
    func conferenceById_notFound() {
        let conference = LeagueStructure.conference(byId: "unknown_conference")

        #expect(conference == nil)
    }

    // MARK: - Specification Tests: stadiumCount(for:)

    @Test("stadiumCount(for:): MLB returns 30")
    func stadiumCount_mlb() {
        #expect(LeagueStructure.stadiumCount(for: .mlb) == 30)
    }

    @Test("stadiumCount(for:): NBA returns 30")
    func stadiumCount_nba() {
        #expect(LeagueStructure.stadiumCount(for: .nba) == 30)
    }

    @Test("stadiumCount(for:): NHL returns 32")
    func stadiumCount_nhl() {
        #expect(LeagueStructure.stadiumCount(for: .nhl) == 32)
    }

    @Test("stadiumCount(for:): MLS returns 0")
    func stadiumCount_mls() {
        #expect(LeagueStructure.stadiumCount(for: .mls) == 0)
    }

    // MARK: - Invariant Tests

    @Test("Invariant: MLB has 3 AL + 3 NL divisions")
    func invariant_mlbDivisionStructure() {
        let divisions = LeagueStructure.divisions(for: .mlb)

        let alDivisions = divisions.filter { $0.conferenceId == "mlb_al" }
        let nlDivisions = divisions.filter { $0.conferenceId == "mlb_nl" }

        #expect(alDivisions.count == 3)
        #expect(nlDivisions.count == 3)
    }

    @Test("Invariant: NBA has 3 Eastern + 3 Western divisions")
    func invariant_nbaDivisionStructure() {
        let divisions = LeagueStructure.divisions(for: .nba)

        let eastern = divisions.filter { $0.conferenceId == "nba_eastern" }
        let western = divisions.filter { $0.conferenceId == "nba_western" }

        #expect(eastern.count == 3)
        #expect(western.count == 3)
    }

    @Test("Invariant: NHL has 2 Eastern + 2 Western divisions")
    func invariant_nhlDivisionStructure() {
        let divisions = LeagueStructure.divisions(for: .nhl)

        let eastern = divisions.filter { $0.conferenceId == "nhl_eastern" }
        let western = divisions.filter { $0.conferenceId == "nhl_western" }

        #expect(eastern.count == 2)
        #expect(western.count == 2)
    }

    @Test("Invariant: each conference contains valid division IDs")
    func invariant_conferenceContainsValidDivisions() {
        for conference in LeagueStructure.conferences {
            for divisionId in conference.divisionIds {
                let division = LeagueStructure.division(byId: divisionId)
                #expect(division != nil, "Division \(divisionId) in conference \(conference.id) should exist")
                #expect(division?.conferenceId == conference.id, "Division \(divisionId) should belong to conference \(conference.id)")
            }
        }
    }

    @Test("Invariant: all divisions have unique IDs")
    func invariant_uniqueDivisionIds() {
        let allDivisions = LeagueStructure.mlbDivisions + LeagueStructure.nbaDivisions + LeagueStructure.nhlDivisions
        let ids = allDivisions.map { $0.id }
        let uniqueIds = Set(ids)

        #expect(ids.count == uniqueIds.count, "Division IDs should be unique")
    }

    @Test("Invariant: all conferences have unique IDs")
    func invariant_uniqueConferenceIds() {
        let ids = LeagueStructure.conferences.map { $0.id }
        let uniqueIds = Set(ids)

        #expect(ids.count == uniqueIds.count, "Conference IDs should be unique")
    }
}
