import Foundation

final class WeddingTable: AbstractTable<WeddingPlayer> {

    var indexOfWeddingOffer: Int

    init(table: BiddingTable, offerer: BiddingPlayer) {
        let players = table.players.map { WeddingPlayer(player: $0, offersWedding: $0 == offerer) }
        indexOfWeddingOffer = table.players.index(of: offerer)
        super.init(table: table, players: players)
    }

    var hasRemainingActors: Bool {
        players.contains { $0.requiresAction }
    }

    var requiresCardSelection: Bool {
        players.contains { $0.selectsGame }
    }

    override var playedGame: GameType? {
        .hochzeit
    }

    override func perform(action: PlayerAction, forPlayer name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
        guard let player = players.player(named: name) else {
            print("Player \(name) unexpectedly missing from wedding table \(self.name)")
            return (.tableStateInvalid, nil)
        }
        guard player.canPerform(action) else {
            return (.tableStateInvalid, nil)
        }

        switch action {
        case .acceptWedding:
            return performWeddingAccept(forPlayer: player)
        case .withdrawFromAuction:
            return performWithdrawl(forPlayer: player)
        case .increaseOrMatchGame:
            fatalError()
        default:
            return (.tableStateInvalid, nil)
        }
    }

    private func performWeddingAccept(forPlayer player: WeddingPlayer) -> (result: PlayerActionResult, table: ManageableTable?) {
        guard player.requiresAction else {
            return (.tableStateInvalid, nil)
        }
        player.state = .wouldAcceptWedding
        guard !hasRemainingActors else {
            return (.success, nil)
        }
        // Nobody wants to play a higher game, so let the first player accept the wedding
        players.first { $0.wouldAcceptWedding }!.selectsGame = true
        return (.success, nil)
    }

    private func performWithdrawl(forPlayer player: WeddingPlayer) -> (result: PlayerActionResult, table: ManageableTable?) {
        guard player.requiresAction else {
            return (.tableStateInvalid, nil)
        }
        player.state = .withdrawnFromAuction
        guard !hasRemainingActors else {
            return (.success, nil)
        }
        // Nobody wants to play a higher game, so let the first player accept the wedding
        guard let player = players.first(where: { $0.wouldAcceptWedding }) else {
            // Nobody wants to accept the wedding or play something higher, so abort the game
            let table = WaitingTable(oldTableAdvancedByOne: self)
            return (.success, table)
        }
        player.selectsGame = true
        return (.success, nil)
    }

    private func performOutbid(forPlayer player: WeddingPlayer) -> (result: PlayerActionResult, table: ManageableTable?) {
        guard player.requiresAction else {
            return (.tableStateInvalid, nil)
        }
        let table = BiddingTable(wedding: self, outbidBy: player)
        return (.success, table)
    }

    override func cards(forPlayerAt index: Int) -> [PlayableCard] {
        let player = players[index]
        guard requiresCardSelection, player.selectsGame else {
            return player.cards.unplayable
        }
        return player.exchangeableCards
    }

    override func gameIsSelected(byPlayerAt index: Int) -> Bool {
        players[index].selectsGame
    }

    override func play(card: Card, player name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
        guard requiresCardSelection else {
            print("Wedding is not in stage where card should be selected")
            return (.tableStateInvalid, nil)
        }
        guard let player = players.player(named: name) else {
            print("Player \(name) unexpectedly missing from wedding table \(self.name)")
            return (.tableStateInvalid, nil)
        }
        guard player.selectsGame else {
            print("Player \(name) is not the one selecting a wedding card")
            return (.tableStateInvalid, nil)
        }
        guard player.canExchange(card: card) else {
            print("Invalid card \(card) to exchange in wedding")
            return (.tableStateInvalid, nil)
        }

        let offerer = players[indexOfWeddingOffer]
        let trumpCard = offerer.replaceWeddingCard(with: card)
        player.replace(card, with: trumpCard)
        let table = PlayingTable(wedding: self, offeredBy: offerer, acceptedBy: player)
        return (.success, table)
    }

}
