import Testing
@testable import SoliCards

@Suite("FreeCell Rules Tests")
struct FreeCellRulesTests {
    let rules = FreeCellRules()

    @Test("Deal distributes all 52 cards face-up")
    func dealLayout() {
        let deck = Deck.standard()
        let snapshot = rules.deal(deck: deck)

        #expect(snapshot.tableaus.count == 8)
        // First 4 tableaus: 7 cards, last 4: 6 cards
        for i in 0..<4 {
            #expect(snapshot.tableaus[i].count == 7)
        }
        for i in 4..<8 {
            #expect(snapshot.tableaus[i].count == 6)
        }
        // All face-up
        let allCards = snapshot.tableaus.flatMap { $0 }
        #expect(allCards.count == 52)
        #expect(allCards.allSatisfy { $0.isFaceUp })

        // 4 empty free cells
        #expect(snapshot.freeCells.count == 4)
        #expect(snapshot.freeCells.allSatisfy { $0 == nil })

        // No stock
        #expect(snapshot.stock.isEmpty)
    }

    @Test("Power move calculation")
    func powerMoves() {
        // (1 + empty_freecells) × 2^empty_tableaus
        var snapshot = emptySnapshot()
        // All 4 free cells empty, target is empty (so 7 empty tableaus count)
        let max1 = rules.calculateMaxMovableCards(state: snapshot, targetEmpty: true)
        // (1 + 4) × 2^7 = 5 × 128 = 640
        #expect(max1 == 640)

        // 2 free cells occupied, no empty tableaus
        snapshot.freeCells = [Card(suit: .hearts, rank: .ace, isFaceUp: true),
                              Card(suit: .spades, rank: .two, isFaceUp: true),
                              nil, nil]
        for i in 0..<8 {
            snapshot.tableaus[i] = [Card(suit: .clubs, rank: .king, isFaceUp: true)]
        }
        let max2 = rules.calculateMaxMovableCards(state: snapshot, targetEmpty: false)
        // (1 + 2) × 2^0 = 3
        #expect(max2 == 3)
    }

    @Test("Can move single card to empty free cell")
    func moveToFreeCell() {
        let card = Card(suit: .hearts, rank: .five, isFaceUp: true)
        var snapshot = emptySnapshot()
        snapshot.tableaus[0] = [card]

        #expect(rules.canMove(cards: [card], from: .tableau(0), to: .freeCell(0), state: snapshot))
    }

    @Test("Cannot move multiple cards to free cell")
    func multipleToFreeCell() {
        let card1 = Card(suit: .spades, rank: .six, isFaceUp: true)
        let card2 = Card(suit: .hearts, rank: .five, isFaceUp: true)
        var snapshot = emptySnapshot()
        snapshot.tableaus[0] = [card1, card2]

        #expect(!rules.canMove(cards: [card1, card2], from: .tableau(0), to: .freeCell(0), state: snapshot))
    }

    // MARK: - Helpers

    private func emptySnapshot() -> GameSnapshot {
        GameSnapshot(
            tableaus: Array(repeating: [], count: 8),
            foundations: [[], [], [], []],
            stock: [],
            waste: [],
            freeCells: [nil, nil, nil, nil],
            moves: 0,
            score: 0
        )
    }
}
