//
// BracketModel.swift
// IceGlass
//
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
//

import Foundation

struct PlayoffBracket: Codable {
    let series: [Series]

    struct Series: Codable {
        let seriesUrl: String?
        let seriesLetter: String
        let playoffRound: Int
        let seriesTitle: String
        let topSeedTeam: Team?
        let bottomSeedTeam: Team?
        let topSeedWins: Int
        let bottomSeedWins: Int

        struct Team: Codable {
            let abbrev: String
        }

        /// True once both teams are known (i.e. the series is actually matched up).
        var isMatched: Bool { topSeedTeam != nil && bottomSeedTeam != nil }

        var isOver: Bool { topSeedWins == 4 || bottomSeedWins == 4 }

        /// 1-based game number of the next unplayed game in the series (nil if series is over).
        var nextGameNumber: Int? {
            isOver ? nil : topSeedWins + bottomSeedWins + 1
        }

        /// Abbrev of whichever side has reached 4 wins, nil if series ongoing.
        var winner: String? {
            guard isOver else { return nil }
            return topSeedWins == 4 ? topSeedTeam?.abbrev : bottomSeedTeam?.abbrev
        }

        /// Abbrev of the eliminated side, nil if the series is ongoing.
        var loser: String? {
            guard isOver else { return nil }
            return topSeedWins == 4 ? bottomSeedTeam?.abbrev : topSeedTeam?.abbrev
        }

        /// Absolute URL for the NHL.com series page, or nil if the bracket doesn't provide one.
        var fullSeriesUrl: String? {
            seriesUrl.map { "https://www.nhl.com\($0)" }
        }

        func involves(away: String, home: String) -> Bool {
            guard let top = topSeedTeam, let bottom = bottomSeedTeam else { return false }
            let pair = Set([away, home])
            return pair == Set([top.abbrev, bottom.abbrev])
        }
    }

    /// Lowest round number that still has at least one matched, live series.
    var currentRound: Int? {
        series
            .filter { $0.isMatched && !$0.isOver }
            .map(\.playoffRound)
            .min()
    }

    /// All matched series in the current round.
    var currentRoundSeries: [Series] {
        guard let round = currentRound else { return [] }
        return matchedSeries(inRound: round)
    }

    /// Rounds that have at least one matched series, ascending (1…current).
    var activeRounds: [Int] {
        Set(series.filter(\.isMatched).map(\.playoffRound)).sorted()
    }

    /// All matched series in the given round, sorted by series letter.
    func matchedSeries(inRound round: Int) -> [Series] {
        series
            .filter { $0.playoffRound == round && $0.isMatched }
            .sorted { $0.seriesLetter < $1.seriesLetter }
    }

    func series(for game: Scoreboard.Game) -> Series? {
        series.first { $0.involves(away: game.awayTeam.abbrev, home: game.homeTeam.abbrev) }
    }
}
