import Foundation

protocol CardOrder {

    static var trumpCount: Int { get }

    static var cardOrder: [Card] { get }
}

extension CardOrder {

    private static var sortIndex: [Card : Int] {
        cardOrder.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset }
    }

    private static var trumpOrder: [Card] {
        Array(cardOrder[0..<trumpCount])
    }

    private static var trumps: Set<Card> { Set(trumpOrder)
    }

    static func isTrump(_ card: Card) -> Bool {
        trumps.contains(card)
    }

    static func highCardIndex(cards: [Card]) -> Int {
        let high: Card
        if hasTrump(in: cards) {
            high = sort(cards).first!
        } else {
            let suit = cards.first!.suit
            high = cards.filter { $0.suit == suit }
                .sorted { $0.symbol }.first!
        }
        return cards.firstIndex(of: high)!
    }

    static func consecutiveTrumps(_ cards: [Card]) -> Int {
        var count = 0
        while cards.contains(trumpOrder[count]) {
            count += 1
        }
        guard count >= 3 else {
            return 0
        }
        return count
    }

    static func sort(_ cards: [Card]) -> [Card] {
        cards.sorted { sortIndex[$0]! < sortIndex[$1]! }
    }

    static func trumpCount(_ cards: [Card]) -> Int {
        cards.filter { trumps.contains($0) }.count
    }

    static func hasTrump(in cards: [Card]) -> Bool {
        cards.contains { trumps.contains($0) }
    }

    static func has(suit: Card.Suit, in cards: [Card]) -> Bool {
        cards.contains { !isTrump($0) && $0.suit == suit }
    }

    static func cards(with suit: Card.Suit, in cards: [Card]) -> [Card] {
        cards.filter { !isTrump($0) && $0.suit == suit }
    }

    static func hasCardToCall(_ suit: Card.Suit, in cards: [Card]) -> Bool {
        cards.contains { $0.symbol != .ass && $0.suit == suit && !isTrump($0) }
    }
}
