import Foundation
import PokerCore

/// A placeholder decision-maker so the engine can be watched playing something that looks like
/// poker. The real opponents — personalities, skill dial, adjusting to the player — are phase 2.
struct SimpleActor {
    var rng: SeededRandom

    mutating func act(_ legal: LegalActions, _ engine: HandEngine) -> PlayerAction {
        let seat = engine.seats[legal.seat]
        let opponents = max(1, engine.seats.filter { $0.isInHand && $0.seat != legal.seat }.count)
        let equity = EquityCalculator.equity(
            hero: seat.hole,
            opponentCount: opponents,
            board: engine.board,
            trials: 400,
            rng: &rng
        ).equity

        // Roughly the share of the pot needed to make calling break even.
        let potOdds = legal.callCost > 0 ? Double(legal.callCost) / Double(engine.pot + legal.callCost) : 0

        if equity > 0.72, legal.canBetOrRaise {
            let target = min(legal.maxRaiseTo, max(legal.minRaiseTo, engine.pot * 2 / 3))
            return legal.canBet ? .bet(to: target) : .raise(to: target)
        }
        if legal.canCheck { return .check }
        if equity > potOdds + 0.04 { return .call }
        return .fold
    }
}

func describe(_ engine: HandEngine, seatNames: [Int: String]) -> String {
    let positions = engine.positions()
    var lines: [String] = []
    for seat in engine.seats where seat.status != .sittingOut {
        let pos = positions[seat.seat].map { " (\($0.rawValue))" } ?? ""
        let mark = engine.actor == seat.seat ? "→" : " "
        let state = seat.status == .folded ? "folded" : (seat.status == .allIn ? "all in" : "\(seat.stack)")
        lines.append("  \(mark) \(seatNames[seat.seat] ?? "P\(seat.seat)")\(pos)  \(seat.hole.shorthand)  \(state)")
    }
    return lines.joined(separator: "\n")
}

func playHand(_ engine: HandEngine, actor: inout SimpleActor, verbose: Bool, seatNames: [Int: String]) {
    var lastStreet: Street? = nil
    while !engine.isComplete, let legal = engine.legalActions() {
        if verbose, lastStreet != engine.street {
            lastStreet = engine.street
            let board = engine.board.isEmpty ? "" : "   board \(engine.board.shorthand)"
            print("  — \(engine.street.name), pot \(engine.pot)\(board)")
        }
        let action = actor.act(legal, engine)
        let name = seatNames[legal.seat] ?? "P\(legal.seat)"
        try! engine.apply(action)
        if verbose { print("      \(name) \(action)") }
    }
}

func runSingleHand(seed: UInt64, players: Int) {
    let seats = (0..<players).map { SeatState(seat: $0, playerID: "P\($0)", stack: 10_000) }
    let names = Dictionary(uniqueKeysWithValues: seats.map { ($0.seat, $0.playerID) })
    let config = HandConfig(level: BlindLevel(smallBlind: 50, bigBlind: 100, ante: .bigBlindAnte(100)),
                            buttonSeat: 0, seed: seed)
    let engine = HandEngine(seats: seats, config: config)

    print("Hand seed \(seed) — \(players) players, blinds 50/100")
    print(describe(engine, seatNames: names))
    print("")

    var actor = SimpleActor(rng: SeededRandom(seed: seed &* 31))
    playHand(engine, actor: &actor, verbose: true, seatNames: names)

    let result = engine.result!
    print("")
    print("  board \(result.board.shorthand)")
    for pot in result.pots {
        print("  pot \(pot.amount)\(pot.isSide ? " (side)" : "")  contested by \(pot.eligible.map { names[$0] ?? "" }.joined(separator: ", "))")
    }
    for award in result.awards {
        let hand = award.hand.map { " with \($0)" } ?? " (everyone folded)"
        print("  \(names[award.seat] ?? "") wins \(award.amount)\(hand)")
    }
}

func runTournament(players: Int, seed: UInt64, verbose: Bool) {
    let structure = TournamentStructure.standard(startingStack: 10_000, handsPerLevel: 15, placesPaid: 3, entrants: players)
    let tourney = Tournament(structure: structure, playerIDs: (1...players).map { "Player \($0)" }, seed: seed)
    var actor = SimpleActor(rng: SeededRandom(seed: seed &* 7 &+ 1))
    var hands = 0
    var lastLevel = -1

    print("Tournament — \(players) players, \(structure.startingStack) chips each, \(structure.placesPaid) paid")
    print("")

    while let (seats, config) = tourney.nextHandSeats() {
        let names = Dictionary(uniqueKeysWithValues: seats.map { ($0.seat, $0.playerID) })
        let engine = HandEngine(seats: seats, config: config)

        let levelIndex = min(tourney.handNumber / structure.handsPerLevel, structure.levels.count - 1)
        if levelIndex != lastLevel {
            lastLevel = levelIndex
            let level = tourney.currentLevel
            print("Level \(levelIndex + 1): blinds \(level.smallBlind)/\(level.bigBlind) — \(tourney.alive.count) left")
        }

        if verbose {
            print("")
            print("Hand \(hands + 1)")
            print(describe(engine, seatNames: names))
        }

        playHand(engine, actor: &actor, verbose: verbose)
        let before = Dictionary(uniqueKeysWithValues: seats.map { ($0.playerID, $0.stack) })
        tourney.recordHand(finalStacks: engine.result!.finalStacks, seatIDs: names)

        for player in tourney.players where player.finishedPlace != nil && (before[player.id] ?? 0) > 0 && player.stack == 0 {
            print("   \(player.id) busts in \(ordinal(player.finishedPlace!)) place  (hand \(hands + 1))")
        }

        hands += 1
        if hands > 10_000 { print("stopping: tournament ran long"); break }
    }

    print("")
    print("Finished after \(hands) hands.")
    let payouts = tourney.payouts()
    for player in tourney.players.sorted(by: { ($0.finishedPlace ?? 99) < ($1.finishedPlace ?? 99) }).prefix(structure.placesPaid) {
        let share = payouts[player.id].map { String(format: "%.0f%% of the prize pool", $0 * 100) } ?? ""
        print("  \(ordinal(player.finishedPlace ?? 0)): \(player.id)  \(share)")
    }
}

private func playHand(_ engine: HandEngine, actor: inout SimpleActor, verbose: Bool) {
    let names = Dictionary(uniqueKeysWithValues: engine.seats.map { ($0.seat, $0.playerID) })
    playHand(engine, actor: &actor, verbose: verbose, seatNames: names)
}

func ordinal(_ n: Int) -> String {
    switch n {
    case 1: return "1st"
    case 2: return "2nd"
    case 3: return "3rd"
    default: return "\(n)th"
    }
}
