PLEASE FIX THE CODE
///////////////////
class Card {
    constructor(suit, rank) {
        this.suit = suit;
        this.rank = rank;
    }
}
//////////////////
function determineWinners(playerHands, boardCards) {
    const allHands = playerHands.map((hand) => [...hand, ...boardCards]);
    const bestHands = allHands.map((hand) => getBestHand(hand));
    const maxRank = Math.max(...bestHands.map((hand) => hand.rank));

    return bestHands.reduce((winners, hand, index) => {
        if (hand.rank === maxRank) {
            winners.push({
                playerId: index,
                winningCombinationId: hand.combination.id,
                winningCards: hand.cards,
            });
        }
        return winners;
    }, []);
}

function getBestHand(cards) {
    const combinations = [RoyalFlush, StraightFlush, FourOfAKind, FullHouse, Flush, Straight, ThreeOfAKind, TwoPairs, Pair];

    for (const Combination of combinations) {
        const result = Combination.test(cards);
        if (result != null) {
            return { rank: Combination.rank, combination: result.combination, cards: result.cards };
        }
    }
}

class Combination {
    constructor(name, id, rank) {
        this.name = name;
        this.id = id;
        this.rank = rank;
    }

    static test(cards) {}
}

class Pair extends Combination {
    constructor() {
        super("Pair", 1, 1);
    }

    static test(cards) {
        let pairsArr = [];
        for (let i = 0; i < cards.length - 1; i++) {
            if (cards[i].rank === cards[i + 1].rank) {
                pairsArr.push([cards[i], cards[i + 1]]);
            }
        }

        let remainingCards = [],
            pair = null;

        if (pairsArr.length > 0) {
            for (let i = 0; i < pairsArr[0].length; i++) {
                pair = pairsArr[0][i];
                cards.filter((card) => {
                    if (!(card === pair)) {
                        remainingCards.push(card);
                    }
                });
            }

            let count = 0;
            while (remainingCards.length > 3 && count < 2) {
                count++;
                for (let i = 0; i < remainingCards.length - 1; i++) {
                    if (remainingCards[i].rank === remainingCards[i + 1].rank) {
                        pair2 = [remainingCards[i], remainingCards[i + 1]];
                        pairsArr.push([pair, pair2]);
                        let holder = [];
                        for (let i = 0; i < remainingCards.length; i++) {
                            if (!pair.includes(remainingCards[i])) {
                                holder.push(remainingCards[i]);
                            }
                        }

                        remainingCards = holder;
                        break;
                    }
                }
            }
        }

        let output = {
            combination: this,
            cards: [].concat.apply([], pairsArr),
        };

        return output;
    }
}

class TwoPairs extends Combination {
    constructor() {
        super("Two Pairs", 2, 2);
    }

    static test(cards) {
        let pairsArr = [];
        for (let i = 0; i < cards.length - 1; i++) {
            if (cards[i].rank === cards[i + 1].rank) {
                pairsArr.push([cards[i], cards[i + 1]]);
            }
        }

        let holder = [],
            pair = null;

        while (pairsArr.length > 1) {
            pair = pairsArr[0][0];
            pair2 = pairsArr[1][0];

            pairsArr.shift();
            pairsArr.shift();

            cards.filter((card) => {
                if (!(card === pair || card === pair2)) {
                    holder.push(card);
                }
            });

            let newPair = [];
            for (let j = holder.length - 1; j > 0; j--) {
                if (holder[j].rank === holder[j - 1].rank) {
                    newPair = [holder[j], holder[j - 1]];
                    pairsArr.push([pair, pair2, newPair]);
                    break;
                }
            }

            if (pairsArr.length === 0 && holder.length >= 4) {
                pairsArr = this.getPossibleTwoPairs(pair, pair2, holder);
                break;
            } else if (holder.length < 4) {
                break;
            }
        }

        let output = {
            combination: this,
            cards: [].concat.apply([], pairsArr),
        };

        return output;
    }

    getPossibleTwoPairs(firstPairCardA, firstPairCardB, otherCards) {
        let pairsArr = [];

        for (let i = 0; i < otherCards.length; i++) {
            //nested loop comparing each card with the items after it in the array
            for (let j = i + 1; j < otherCards.length; j++) {
                //if we find a match increment our count and store the indices of each matching element
                if (otherCards[i].rank === otherCards[j].rank) {
                    remainingCards = [];
                    let anotherpair = null;
                    for (let z = 0; z < otherCards.length; z++) {
                        if (!(z == i || z == j)) {
                            remainingCards.push(otherCards[z]);
                        } else {
                            anotherpair = [otherCards[z], otherCards[z + 1]];
                        }
                    }

                    pairsArr.push([firstPairCardA, firstPairCardB, ...anotherpair]);
                }
            }
        }

        return pairsArr;
    }
}

class ThreeOfAKind extends Combination {
    constructor() {
        super("Three of a kind", 3, 3);
    }

    static test(cards) {
        let trios = [];

        for (let i = 0; i <= cards.length - 3; i++) {
            //checking whether values are equal & not undefined(not yet deleted)

            if (cards[i].rank === cards[i + 1].rank && cards[i].rank === cards[i + 2].rank) {
                trios.push([cards[i], cards[i + 1], cards[i + 2]]);
            }
        }

        let remainingCards = [];

        for (let i = 0; i < cards.length; i++) {
            let test = true;
            for (let j = 0; j < trios.length; j++) {
                if (trios[j].indexOf(cards[i]) !== -1) {
                    test = false;
                }
            }

            if (test === true) {
                remainingCards.push(cards[i]);
            }
        }

        if (trios.length < 1 || remainingCards.length < 2) {
            return null;
        } else {
            let output = {
                combination: this,
                cards: [].concat.apply([], trios).concat(remainingCards.slice(-2)),
            };

            return output;
        }
    }
}

class Straight extends Combination {
    constructor() {
        super("Straight", 4, 4);
    }

    static test(cards) {
        let sorted_rank = [...new Set(cards.map((card) => card.rank))].sort((a, b) => Number(b) - Number(a));

        const Ace = sorted_rank.includes("A") && sorted_rank.includes("K");
        const Sequentialized_Ace = ["5", "4", "3", "2"].includes(sorted_rank[0]) && Ace;

        if ((!Ace && sorted_rank.length < 5) || (!Sequentialized_Ace && sorted_rank.length < 5)) {
            return null;
        }

        if (Sequentialized_Ace) {
            sorted_rank.shift();
            sorted_rank.push("14");
        }

        let rank_index = 0,
            possible_straight = [sorted_rank[0]];

        for (let i = 1; i < sorted_rank.length; i++) {
            if (Number(sorted_rank[i - 1]) === Number(sorted_rank[i]) + 1) {
                possible_straight.push(sorted_rank[i]);
            } else if (Number(sorted_rank[i - 1]) !== Number(sorted_rank[i])) {
                rank_index = i;
                possible_straight = [sorted_rank[rank_index]];
            }

            let remainingCards = [];
            for (let i = 0; i < cards.length; i++) {
                let test = true;
                for (let j = 0; j < possible_straight.length; j++) {
                    if (possible_straight[j] === cards[i].rank) {
                        possible_straight.splice(j, 1);
                        test = false;
                    }
                }

                if (test === true) {
                    remainingCards.push(cards[i]);
                }
            }

            if (possible_straight.length === 0) {
                let straightArr = [];
                for (let j = 0; j < cards.length; j++) {
                    if (sorted_rank.indexOf(cards[j].rank) !== -1) {
                        straightArr.push(cards[j]);
                    }
                }

                return {
                    combination: this,
                    cards: straightArr.slice(0, 5),
                };
            }
        }

        if (rank_index >= sorted_rank.length - 4 && ["A", "5"].includes(sorted_rank[0])) {
            let index = sorted_rank.indexOf("A");
            let Ace = sorted_rank.splice(index, 1);
            Ace[0] = "14";

            possible_straight.unshift(Ace.toString());

            rank_index = 0;

            for (let i = 1; i <= sorted_rank.length - 3; i++) {
                if (parseInt(sorted_rank[rank_index]) - parseInt(sorted_rank[rank_index + 3]) <= 4) {
                    for (let j = i + 1; j <= i + 2; j++) {
                        if (parseInt(sorted_rank[i]) - parseInt(sorted_rank[j]) == 1 && parseInt(sorted_rank[j]) - parseInt(sorted_rank[j + 1]) == 1) {
                            possible_straight = [sorted_rank[rank_index], sorted_rank[i], sorted_rank[j], sorted_rank[j + 1]];
                        }
                    }
                }
                rank_index++;
            }

            let remainingCards = [];
            for (let i = 0; i < cards.length; i++) {
                let test = true;
                for (let j = 0; j < possible_straight.length; j++) {
                    if (possible_straight[j] === cards[i].rank) {
                        possible_straight.splice(j, 1);
                        test = false;
                    }
                }

                if (test === true) {
                    remainingCards.push(cards[i]);
                }
            }

            if (possible_straight.length === 0) {
                let straightArr = [];
                for (let j = 0; j < cards.length; j++) {
                    if (sorted_rank.indexOf(cards[j].rank) !== -1) {
                        straightArr.push(cards[j]);
                    }
                }

                return {
                    combination: this,
                    cards: straightArr.slice(0, 5),
                };
            }
        }

        return null;
    }
}

class Flush extends Combination {
    constructor() {
        super("Flush", 5, 5);
    }

    static test(cards) {
        const counts = {};

        for (let card of cards) {
            counts[card.suit] = (counts[card.suit] || []).concat(card);
        }

        const flusharr = Object.keys(counts).filter((key, i, arr) => {
            if (counts[key].length >= 5) {
                return key;
            }
        });

        if (flusharr.length !== 1) {
            return null;
        }

        let flushCards = counts[flusharr];

        let output = {
            combination: this,
            cards: flushCards.slice(0, 5),
        };

        return output;
    }
}

class FullHouse extends Combination {
    constructor() {
        super("Full house", 6, 6);
    }

    static test(cards) {
        const trioCardsArr = ThreeOfAKind.test(cards)?.cards;
        const pairsArr = Pair.test(cards)?.cards;

        if (trioCardsArr && pairsArr) {
            trioIndex1 = cards.indexOf(trioCardsArr[0]);
            trioIndex2 = cards.indexOf(trioCardsArr[2]);

            let houseArray = [].concat(pairsArr, trioCardsArr);
            let remainders = [];

            for (let i = 0; i < cards.length; i++) {
                let test = true;
                for (let j = 0; j < houseArray.length; j++) {
                    if (houseArray[j] === cards[i]) {
                        houseArray.splice(j, 1);
                        test = false;
                    }
                }

                if (test === true) {
                    remainders.push(cards[i]);
                }
            }

            return {
                combination: this,
                cards: [].concat.apply([], [cards[trioIndex1], cards[trioIndex1 + 1], cards[trioIndex2], cards[trioIndex2 + 1], houseArray.slice(0, 1)]),
            };
        } else {
            return null;
        }
    }
}

class FourOfAKind extends Combination {
    constructor() {
        super("four of a kind", 7, 7);
    }

    static test(cards) {
        const counts = {};

        for (let card of cards) {
            counts[card.rank] = (counts[card.rank] || []).concat(card);
        }

        let output = null;

        Object.keys(counts).forEach(function (key) {
            if (counts[key].length >= 4) {
                let remaining = [];
                for (let i = 0; i < cards.length; i++) {
                    if (!(key === cards[i].rank)) {
                        remaining.push(cards[i]);
                    }
                }

                output = {
                    combination: this,
                    cards: [].concat.apply([], [counts[key], remaining.slice(0, 1)]),
                };
            }
        });

        return output;
    }
}

class StraightFlush extends Combination {
    constructor() {
        super("Straight flush", 8, 8);
    }

    static test(cards) {
        const counts = {};

        for (let card of cards) {
            counts[card.suit] = (counts[card.suit] || []).concat(card);
        }

        const flusharr = Object.keys(counts).filter((key, i, arr) => {
            if (counts[key].length >= 5) {
                return key;
            }
        });

        if (flusharr.length !== 1) {
            return null;
        }

        let flushCards = counts[flusharr];

        let sorted_rank = [...new Set(flushCards.map((card) => card.rank))].sort((a, b) => Number(b) - Number(a));

        const Ace = sorted_rank.includes("A") && sorted_rank.includes("K");
        const Sequentialized_Ace = ["5", "4", "3", "2"].includes(sorted_rank[0]) && Ace;

        if ((!Ace && sorted_rank.length < 5) || (!Sequentialized_Ace && sorted_rank.length < 5)) {
            return null;
        }

        if (Sequentialized_Ace) {
            sorted_rank.shift();
            sorted_rank.push("14");
        }

        let rank_index = 0,
            possible_straight = [sorted_rank[0]];

        for (let i = 1; i < sorted_rank.length; i++) {
            if (Number(sorted_rank[i - 1]) === Number(sorted_rank[i]) + 1) {
                possible_straight.push(sorted_rank[i]);
            } else if (Number(sorted_rank[i - 1]) !== Number(sorted_rank[i])) {
                rank_index = i;
                possible_straight = [sorted_rank[rank_index]];
            }

            let remainingCards = [];
            for (let i = 0; i < flushCards.length; i++) {
                let test = true;
                if (possible_straight.indexOf(flushCards[i].rank) !== -1) {
                    possible_straight.splice(possible_straight.indexOf(flushCards[i].rank), 1);
                    test = false;
                }

                if (test === true) {
                    remainingCards.push(flushCards[i]);
                }
            }

            if (possible_straight.length === 0) {
                let straightArr = [];
                for (let j = 0; j < flushCards.length; j++) {
                    if (sorted_rank.indexOf(flushCards[j].rank) !== -1) {
                        straightArr.push(flushCards[j]);
                    }
                }

                return {
                    combination: this,
                    cards: straightArr.slice(0, 5),
                };
            }
        }

        return null;
    }
}

class RoyalFlush extends Combination {
    constructor() {
        super("Royal flush", 9, 9);
    }

    static test(cards) {
        const counts = {};

        for (let card of cards) {
            counts[card.suit] = (counts[card.suit] || []).concat(card);
        }

        const flusharr = Object.keys(counts).filter((key, i, arr) => {
            if (counts[key].length >= 5) {
                return key;
            }
        });

        if (flusharr.length !== 1) {
            return null;
        }

        const flushcards = counts[flusharr][0];

        const sorted_rank = [...new Set(flushcards.map((card) => card.rank))].sort().join("");

        const seq_pat = "AJKQT";

        if (sorted_rank === seq_pat && cards.filter((c) => c.suit === flushcards[0].suit).length >= 5) {
            return {
                combination: this,
                cards: flushcards.filter((card) => seq_pat.indexOf(card.rank) !== -1),
            };
        } else {
            return null;
        }

        return output;
    }
}