import Foundation

protocol GameRules: Sendable {
    var variant: GameVariant { get }

    /// Deal cards from a shuffled deck into the initial board layout.
    func deal(deck: [Card]) -> GameSnapshot

    /// Check if moving the given cards from source to destination is valid.
    func canMove(cards: [Card], from: CardLocation, to: CardLocation, state: GameSnapshot) -> Bool

    /// Return all valid destinations for the given cards from a source location.
    func validDestinations(for cards: [Card], from: CardLocation, state: GameSnapshot) -> [CardLocation]

    /// Draw card(s) from the stock pile.
    func drawFromStock(state: inout GameSnapshot, drawCount: Int) -> MoveAction?

    /// Calculate the score change for a given move.
    func scoreForMove(from: CardLocation, to: CardLocation) -> Int

    /// Check if the game is won.
    func isWon(state: GameSnapshot) -> Bool

    /// Check if auto-complete is possible (all remaining cards are face-up and ordered).
    func canAutoComplete(state: GameSnapshot) -> Bool

    /// Find available hints, ordered by priority (1 = highest).
    func findHints(state: GameSnapshot, settings: DifficultySettings) -> [HintResult]

    /// Check if a card can be stacked onto another card on a tableau.
    func canStackOnTableau(card: Card, onto target: Card) -> Bool

    /// Check if a group of cards can be picked up from a location.
    func canPickUp(cards: [Card], from: CardLocation, state: GameSnapshot) -> Bool
}

extension GameRules {
    func validDestinations(for cards: [Card], from: CardLocation, state: GameSnapshot) -> [CardLocation] {
        var destinations: [CardLocation] = []

        for i in 0..<state.foundations.count {
            if canMove(cards: cards, from: from, to: .foundation(i), state: state) {
                destinations.append(.foundation(i))
            }
        }

        for i in 0..<state.tableaus.count {
            if canMove(cards: cards, from: from, to: .tableau(i), state: state) {
                destinations.append(.tableau(i))
            }
        }

        for i in 0..<state.freeCells.count {
            if canMove(cards: cards, from: from, to: .freeCell(i), state: state) {
                destinations.append(.freeCell(i))
            }
        }

        return destinations
    }
}
