import Foundation
import PokerCore
import PokerCoach
import PokerSession
import PokerTests

/// Where the seat diagnosis's bars come from.
///
///     pokerlab seat-sweep --player control --hands 6000 --seeds 4
///     pokerlab seat-sweep --player badseat4 --hands 3000 --seeds 6
///
/// Runs a player through the app's own nine-handed tournament table and, at each checkpoint of
/// hands, prints every seat's cost per hundred and whether `SeatDiagnosis` would name a worst
/// seat. The control must never be named; the planted player must be named at the seat it was
/// planted in, with its habit leading. `minimumHands` and `requiredMargin` are read off these two
/// runs: the control's widest worst-vs-second gap at a checkpoint is the floor the margin has to
/// clear, and the checkpoint at which the planted seat is found on most seeds is the sample.
func runSeatSweep(playerName: String, hands: Int, seedCount: Int, firstSeed: UInt64, rung: Int) {
    let player = plantedPlayer(named: playerName)
    let checkpoints = [900, 1_350, 2_250, 3_600, 6_300].filter { $0 <= hands } + [hands]
    print("Seat sweep: \(player.name), \(seedCount) seeds × \(hands) hands, rung \(rung)")
    print("cells = seat:mistake-rate%/decisions/hands; gap = worst seat's rate minus the next, in points\n")

    var namedAt: [Int: Int] = [:]
    var gaps: [Int: [Double]] = [:]
    for i in 0..<seedCount {
        let seed = firstSeed &+ UInt64(i) &* 977
        let session = runPlantedTournament(player, hands: hands, seed: seed, rung: rung)
        let all = session.history.hands(in: .allTime).sorted { $0.playedAt < $1.playedAt }
        print("seed \(seed)")
        for point in Array(Set(checkpoints)).sorted() {
            let diagnosis = SeatDiagnosis(of: Array(all.prefix(point)))
            let sorted = diagnosis.seats.filter { $0.decisions >= SeatDiagnosis.minimumDecisions / 2 }
                .sorted { $0.mistakeRate > $1.mistakeRate }
            let cells = diagnosis.seats.map { seat in
                return "\(seat.position):\(Int((seat.mistakeRate * 100).rounded()))%/\(seat.decisions)d/\(seat.hands)h"
            }.joined(separator: " ")
            let gap = sorted.count > 1 ? (sorted[0].mistakeRate - sorted[1].mistakeRate) * 100 : 0
            gaps[point, default: []].append(gap)
            let verdict: String
            if let worst = diagnosis.worst {
                namedAt[point, default: 0] += 1
                let leading = worst.leading.first.map { "\($0.leak.id)×\($0.times)" } ?? "—"
                verdict = "NAMED \(worst.seatPhrase) (\(leading))"
            } else {
                verdict = "not named"
            }
            print("  \(point) hands  gap \(String(format: "%5.1f", gap))  \(verdict)")
            print("     " + cells)
        }
    }
    print("\nsummary (seat named / seeds; widest worst-vs-next gap):")
    for point in Array(Set(checkpoints)).sorted() {
        let widest = gaps[point]?.max() ?? 0
        let mean = (gaps[point] ?? []).reduce(0, +) / Double(max(1, gaps[point]?.count ?? 1))
        print(String(format: "  %5d hands: named %d/%d, gap widest %.1f mean %.1f",
                     point, namedAt[point] ?? 0, seedCount, widest, mean))
    }
}
