package taurus.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class ai {
    private static final Set<Integer> JIANG_PAIS;
    public boolean isTinAi = false;
    public boolean isTingpai = false;
    private Map<String, Double> scoreCache = new HashMap<>();
    private Map<String, List<Integer>> tingCache = new HashMap<>();


    public static void main(String[] args) {
        System.out.println("=== 长沙麻将测试（含碰杠吃牌） ===");
        ai ai1 = new ai();
        System.out.println("\n=== 测试1：普通14张牌 ===");
        PlayerState state1 = new PlayerState();
        state1.handCards = Arrays.asList(209, 209, 208, 207, 206, 205, 203, 202, 202, 202, 108, 105, 104, 103);
        int discard1 = ai1.findBestDiscard(state1);
        System.out.println("推荐出牌: " + ai1.formatCard(discard1));
    }

    public int findBestDiscard(PlayerState state) {
        //参数验证
        if (state == null || state.handCards == null) {
            //throw new IllegalArgumentException("玩家状态和手牌不能为空");
        }
        System.out.println("\n=== 长沙麻将AI出牌分析 ===");
        System.out.println("手牌(" + state.handCards.size() + "张): " + this.formatCards(state.handCards));
        int pongGroups = state.getPongGroupCount();
        int gangGroups = state.getGangGroupCount();
        int chiGroups = state.getChiGroupCount();
        System.out.println("碰牌组数: " + pongGroups + "，杠牌组数: " + gangGroups + "，吃牌组数: " + chiGroups);
        this.scoreCache.clear();
        this.tingCache.clear();
        long startTime = System.currentTimeMillis();
        Map<Integer, DiscardResult> results = new HashMap<>();
        Set<Integer> uniqueCards = new HashSet<>(state.handCards);
        if (state.handCards.size() == 1) {
            int onlyCard = state.handCards.get(0);
            System.out.println("只剩一张牌，必须出: " + this.formatCard(onlyCard));
            return onlyCard;
        } else {
            for(int card : uniqueCards) {
                this.simulateDiscard(state, card);
                DiscardResult result = this.evaluateDiscardWithState(card, state);
                results.put(card, result);
                System.out.printf("打出 %s: 评分=%.3f (听牌=%d张)%n", this.formatCard(card), result.score, result.tingCards.size());
            }

            int bestCard = -1;
            double bestScore = Double.NEGATIVE_INFINITY;
            int maxTingCount = 0;

            for(DiscardResult result : results.values()) {
                if (result.tingCards.size() > maxTingCount) {
                    maxTingCount = result.tingCards.size();
                }
            }

            if (maxTingCount > 0) {
                for(Map.Entry<Integer, DiscardResult> entry : results.entrySet()) {
                    if (((DiscardResult)entry.getValue()).tingCards.size() == maxTingCount && ((DiscardResult)entry.getValue()).score > bestScore) {
                        bestScore = ((DiscardResult)entry.getValue()).score;
                        bestCard = (Integer)entry.getKey();
                    }
                }
            } else {
                for(Map.Entry<Integer, DiscardResult> entry : results.entrySet()) {
                    if (((DiscardResult)entry.getValue()).score > bestScore) {
                        bestScore = ((DiscardResult)entry.getValue()).score;
                        bestCard = (Integer)entry.getKey();
                    }
                }
            }

            long endTime = System.currentTimeMillis();
            System.out.printf("分析耗时: %dms%n", endTime - startTime);
            if (bestCard != -1) {
                DiscardResult bestResult = results.get(bestCard);
                System.out.println("\n\ud83c\udfaf AI推荐出牌: " + this.formatCard(bestCard));
                if (!bestResult.tingCards.isEmpty()) {
                    this.isTinAi = true;
                    this.isTingpai = true;
                    System.out.println("✅ 听牌，可听 " + bestResult.tingCards.size() + " 张牌:");
                    System.out.println("----牌组----" + bestResult.tingCards);
                    
                    //添加同步控制
                    synchronized (ChangShaSuanFaTest.tinCards) {
                        ChangShaSuanFaTest.tinCards.clear();
                        ChangShaSuanFaTest.tinCards.addAll(bestResult.tingCards);
                    }

                    List<String> tingCardsFormatted = bestResult.tingCards.stream()
                        .map(this::formatCard)
                        .collect(Collectors.toList());
                    System.out.println("    " + String.join(" ", tingCardsFormatted));
                }

                System.out.println("综合评分: " + String.format("%.3f", bestResult.score));
            }

            return bestCard;
        }
    }

    private PlayerState simulateDiscard(PlayerState originalState, int discardCard) {
        PlayerState newState = new PlayerState();
        newState.handCards = new ArrayList(originalState.handCards);
        newState.handCards.remove(discardCard);

        for(List<Integer> group : originalState.pongGroups) {
            newState.pongGroups.add(new ArrayList(group));
        }

        for(List<Integer> group : originalState.gangGroups) {
            newState.gangGroups.add(new ArrayList(group));
        }

        for(List<Integer> group : originalState.chiGroups) {
            newState.chiGroups.add(new ArrayList(group));
        }

        newState.isZhuang = originalState.isZhuang;
        return newState;
    }

    private DiscardResult evaluateDiscardWithState(int discardedCard, PlayerState originalState) {
        PlayerState simulatedState = this.simulateDiscard(originalState, discardedCard);
        List<Integer> hand = simulatedState.handCards;
        double totalScore = (double)0.0F;
        totalScore += (double)10.0F;
        double suitConcentrationScore = this.evaluateSuitConcentration(discardedCard, hand);
        totalScore += suitConcentrationScore;
        System.out.println("\n=== 评估 " + this.formatCard(discardedCard) + " ===");
        int originalCount = 0;

        for(int c : originalState.handCards) {
            if (c == discardedCard) {
                ++originalCount;
            }
        }

        System.out.println("  [原始] " + this.formatCard(discardedCard) + " 在原始手牌中有 " + originalCount + " 张");
        int doorType = this.getDoorType(discardedCard, hand, originalState);
        double doorScore = this.evaluateDoorType(doorType, discardedCard);
        totalScore += doorScore;
        System.out.printf("  [门子] 类型=%s, 评分=%.1f%n", this.getDoorTypeName(doorType), doorScore);
        if (this.isInCompleteSequence(discardedCard, originalState.handCards)) {
            System.out.println("  [顺子] " + this.formatCard(discardedCard) + " 在完整顺子中");
            if (originalCount > 1) {
                System.out.println("  [冗余] " + this.formatCard(discardedCard) + " 在顺子中且有多张，加30分鼓励打出");
                totalScore += (double)30.0F;
            } else {
                System.out.println("  [保留] " + this.formatCard(discardedCard) + " 是顺子中的唯一一张，扣15分");
                totalScore -= (double)15.0F;
            }
        }

        double pairScore = this.evaluatePairsAndPongsFinal(discardedCard, originalState);
        totalScore += pairScore;
        if (this.isAbsolutelyLonelyFinal(discardedCard, hand)) {
            totalScore += (double)15.0F;
            System.out.println("  [孤张] " + this.formatCard(discardedCard) + " 是绝对孤张，加15分");
        }

        double structureScore = this.evaluateSimpleStructure(discardedCard, hand);
        totalScore += structureScore;
        List<Integer> tingCards = this.checkTingCardsWithState(simulatedState);
        if (!tingCards.isEmpty()) {
            totalScore += (double)100.0F;
            totalScore += (double)tingCards.size() * (double)50.0F;

            for(int tingCard : tingCards) {
                int value = this.getCardValue(tingCard);
                if (JIANG_PAIS.contains(value)) {
                    totalScore += (double)20.0F;
                }
            }
        }

        System.out.printf("  [总分] %.1f%n", totalScore);
        return new DiscardResult(totalScore, tingCards, 0);
    }

    private double evaluateSuitConcentration(int card, List<Integer> hand) {
        int type = this.getCardType(card);
        this.getCardValue(card);
        int suitCount = 0;

        for(int c : hand) {
            if (this.getCardType(c) == type) {
                ++suitCount;
            }
        }

        double score = (double)0.0F;
        if (suitCount <= 3) {
            score += (double)20.0F;
        } else if (suitCount >= 8) {
            score -= (double)15.0F;
        }

        return score;
    }

    private int getDoorType(int card, List<Integer> hand, PlayerState state) {
        int type = this.getCardType(card);
        int value = this.getCardValue(card);
        if (type > 3) {
            return 0;
        } else {
            int originalCount = 0;

            for(int c : state.handCards) {
                if (c == card) {
                    ++originalCount;
                }
            }

            if (originalCount >= 2) {
                return 4;
            } else {
                if (value == 1 || value == 9) {
                    int adjacent = value == 1 ? card + 1 : card - 1;
                    if (state.handCards.contains(adjacent)) {
                        return 1;
                    }
                }

                for(int other : state.handCards) {
                    if (this.getCardType(other) == type) {
                        int otherValue = this.getCardValue(other);
                        int diff = Math.abs(otherValue - value);
                        if (diff == 1) {
                            if ((value != 1 || otherValue != 2) && (value != 2 || otherValue != 1) && (value != 8 || otherValue != 9) && (value != 9 || otherValue != 8)) {
                                return 3;
                            }

                            return 1;
                        }
                    }
                }

                for(int other : state.handCards) {
                    if (this.getCardType(other) == type) {
                        int otherValue = this.getCardValue(other);
                        int diff = Math.abs(otherValue - value);
                        if (diff == 2) {
                            return 2;
                        }
                    }
                }

                return 0;
            }
        }
    }

    private String getDoorTypeName(int doorType) {
        switch (doorType) {
            case 0:
                return "无门子";
            case 1:
                return "边张门子";
            case 2:
                return "隔张门子";
            case 3:
                return "相邻门子";
            case 4:
                return "对子";
            default:
                return "未知";
        }
    }

    private double evaluateDoorType(int doorType, int card) {
        double score = (double)0.0F;
        int value = this.getCardValue(card);
        int type = this.getCardType(card);
        switch (doorType) {
            case 0:
                if (JIANG_PAIS.contains(value)) {
                    score += (double)20.0F;
                } else if (value != 1 && value != 9) {
                    score += (double)20.0F;
                } else {
                    score += (double)30.0F;
                }
                break;
            case 1:
                score += (double)25.0F;
                break;
            case 2:
                if (value != 2 && value != 8) {
                    if (value != 3 && value != 7) {
                        score += (double)10.0F;
                    } else {
                        score += (double)8.0F;
                    }
                } else {
                    score += (double)5.0F;
                }
                break;
            case 3:
                score -= (double)15.0F;
                break;
            case 4:
                if (JIANG_PAIS.contains(value)) {
                    score -= (double)20.0F;
                } else if (value != 1 && value != 9) {
                    if (value == 9 && type == 1) {
                        score -= (double)15.0F;
                    } else {
                        score -= (double)10.0F;
                    }
                } else {
                    score += (double)10.0F;
                }
        }

        return score;
    }

    private boolean isInCompleteSequence(int card, List<Integer> hand) {
        int type = this.getCardType(card);
        int value = this.getCardValue(card);
        if (type > 3) {
            return false;
        } else {
            int cardCount = Collections.frequency(hand, card);
            if (cardCount > 0) {
                if (value == 1) {
                    int card2 = card + 1;
                    int card3 = card + 2;
                    return hand.contains(card2) && hand.contains(card3);
                }

                if (value == 9) {
                    int card7 = card - 2;
                    int card8 = card - 1;
                    return hand.contains(card7) && hand.contains(card8);
                }

                if (value >= 2 && value <= 8) {
                    int prev1 = card - 1;
                    int prev2 = card - 2;
                    if (value >= 3 && hand.contains(prev1) && hand.contains(prev2)) {
                        return true;
                    }

                    int next1 = card + 1;
                    int next2 = card + 2;
                    if (value <= 7 && hand.contains(next1) && hand.contains(next2)) {
                        return true;
                    }

                    int prev = card - 1;
                    int next = card + 1;
                    if (hand.contains(prev) && hand.contains(next)) {
                        return true;
                    }
                }
            }

            return false;
        }
    }

    private double evaluateSimpleStructure(int card, List<Integer> hand) {
        double score = (double)0.0F;
        int type = this.getCardType(card);
        int value = this.getCardValue(card);
        if (type > 3) {
            return (double)0.0F;
        } else {
            if (JIANG_PAIS.contains(value)) {
                score -= (double)3.0F;
            }

            if (value == 1 || value == 9) {
                score -= (double)2.0F;
            }

            return score;
        }
    }

    private double evaluatePairsAndPongsFinal(int card, PlayerState state) {
        int originalCount = 0;

        for(int c : state.handCards) {
            if (c == card) {
                ++originalCount;
            }
        }

        System.out.println("  [数量] " + this.formatCard(card) + " 在原始手牌中有 " + originalCount + " 张");
        if (originalCount >= 3) {
            System.out.println("  [刻子] " + this.formatCard(card) + " 是刻子，扣50分");
            return (double)-50.0F;
        } else if (originalCount == 2) {
            this.getCardType(card);
            int value = this.getCardValue(card);
            if (value != 1 && value != 9) {
                if (this.isRedundantPairInSequence(card, state.handCards)) {
                    System.out.println("  [冗余] " + this.formatCard(card) + " 是顺子中的冗余对子，加40分鼓励打出");
                    return (double)40.0F;
                } else if (JIANG_PAIS.contains(value)) {
                    System.out.println("  [258将] " + this.formatCard(card) + " 是258将对子，扣30分");
                    return (double)-30.0F;
                } else {
                    System.out.println("  [对子] " + this.formatCard(card) + " 是普通对子，扣10分");
                    return (double)-10.0F;
                }
            } else {
                System.out.println("  [边张对子] " + this.formatCard(card) + " 是边张对子，加60分鼓励打出");
                return (double)60.0F;
            }
        } else {
            System.out.println("  [单张] " + this.formatCard(card) + " 是单张");
            return (double)0.0F;
        }
    }

    private boolean isRedundantPairInSequence(int card, List<Integer> originalHand) {
        int type = this.getCardType(card);
        int value = this.getCardValue(card);
        if (type > 3) {
            return false;
        } else {
            int count = Collections.frequency(originalHand, card);
            if (count < 2) {
                return false;
            } else {
                System.out.println("  [检查冗余] " + this.formatCard(card) + " 是" + count + "张的对子");
                if (value == 1) {
                    int card2 = card + 1;
                    int card3 = card + 2;
                    if (originalHand.contains(card2) && originalHand.contains(card3)) {
                        System.out.println("  [边张冗余] " + this.formatCard(card) + " 是1万且在1-2-3顺子中，应该优先打出");
                        return true;
                    }
                } else if (value == 9) {
                    int card7 = card - 2;
                    int card8 = card - 1;
                    if (originalHand.contains(card7) && originalHand.contains(card8)) {
                        System.out.println("  [边张冗余] " + this.formatCard(card) + " 是9万且在7-8-9顺子中，应该优先打出");
                        return true;
                    }
                }

                if (value == 3) {
                    int card1 = card - 2;
                    int card2 = card - 1;
                    boolean has1 = originalHand.contains(card1);
                    boolean has2 = originalHand.contains(card2);
                    System.out.println("  [顺子检查] 1万=" + has1 + ", 2万=" + has2);
                    if (has1 && has2) {
                        System.out.println("  [确定冗余] " + this.formatCard(card) + " 在1-2-3顺子中是多余的");
                        return true;
                    }
                }

                if (value >= 1 && value <= 7) {
                    int next1 = card + 1;
                    int next2 = card + 2;
                    if (originalHand.contains(next1) && originalHand.contains(next2)) {
                        System.out.println("  [确定冗余] " + this.formatCard(card) + " 在" + value + "-" + this.getCardValue(next1) + "-" + this.getCardValue(next2) + "顺子中是多余的");
                        return true;
                    }
                }

                if (value >= 2 && value <= 8) {
                    int prev = card - 1;
                    int next = card + 1;
                    if (originalHand.contains(prev) && originalHand.contains(next)) {
                        System.out.println("  [确定冗余] " + this.formatCard(card) + " 在" + this.getCardValue(prev) + "-" + value + "-" + this.getCardValue(next) + "顺子中是多余的");
                        return true;
                    }
                }

                if (value >= 3 && value <= 9) {
                    int prev1 = card - 1;
                    int prev2 = card - 2;
                    if (originalHand.contains(prev1) && originalHand.contains(prev2)) {
                        System.out.println("  [确定冗余] " + this.formatCard(card) + " 在" + this.getCardValue(prev2) + "-" + this.getCardValue(prev1) + "-" + value + "顺子中是多余的");
                        return true;
                    }
                }

                return false;
            }
        }
    }

    private boolean isAbsolutelyLonelyFinal(int card, List<Integer> hand) {
        int type = this.getCardType(card);
        int value = this.getCardValue(card);
        if (type > 3) {
            return false;
        } else {
            for(int other : hand) {
                if (this.getCardType(other) == type) {
                    int diff = Math.abs(this.getCardValue(other) - value);
                    if (diff <= 2) {
                        return false;
                    }
                }
            }

            return true;
        }
    }

    public List<Integer> checkTingCardsWithState(PlayerState state) {
        String stateKey = this.getStateKey(state);
        if (this.tingCache.containsKey(stateKey)) {
            return (List)this.tingCache.get(stateKey);
        } else {
            List<Integer> tingCards = new ArrayList();
            int pongGroups = state.getPongGroupCount();
            int gangGroups = state.getGangGroupCount();
            int chiGroups = state.getChiGroupCount();
            int usedCardsCount = pongGroups * 3 + gangGroups * 4 + chiGroups * 3;
            int expectedHandCount = 14 - usedCardsCount - 1;
            if (state.handCards.size() != expectedHandCount) {
                this.tingCache.put(stateKey, tingCards);
                return tingCards;
            } else {
                for(int huCard : this.getReasonableHuCards(state.handCards)) {
                    if (this.canHuWithState(state, huCard)) {
                        tingCards.add(huCard);
                    }
                }

                this.tingCache.put(stateKey, tingCards);
                return tingCards;
            }
        }
    }

    private boolean canHuWithState(PlayerState state, int huCard) {
        int totalHandCards = state.handCards.size() + 1;
        int pongCardsCount = state.getPongGroupCount() * 3;
        int gangCardsCount = state.getGangGroupCount() * 4;
        int chiCardsCount = state.getChiGroupCount() * 3;
        int totalCards = totalHandCards + pongCardsCount + gangCardsCount + chiCardsCount;
        if (totalCards != 14) {
            return false;
        } else {
            List<Integer> remainingHand = new ArrayList(state.handCards);
            remainingHand.add(huCard);
            Collections.sort(remainingHand);
            return this.canHuWith258(remainingHand);
        }
    }

    private boolean canHuWith258(List<Integer> hand) {
        if (hand.size() != 2 && hand.size() != 5 && hand.size() != 8 && hand.size() != 11 && hand.size() != 14) {
            return false;
        } else {
            List<Integer> sortedHand = new ArrayList(hand);
            Collections.sort(sortedHand);

            for(int i = 0; i < sortedHand.size() - 1; ++i) {
                if (((Integer)sortedHand.get(i)).equals(sortedHand.get(i + 1))) {
                    int cardValue = this.getCardValue((Integer)sortedHand.get(i));
                    if (JIANG_PAIS.contains(cardValue)) {
                        List<Integer> tempHand = new ArrayList(sortedHand);
                        tempHand.remove(i);
                        tempHand.remove(i);
                        if (this.canFormMelds(tempHand)) {
                            return true;
                        }
                    }
                }
            }

            return false;
        }
    }

    private boolean canFormMelds(List<Integer> hand) {
        if (hand.isEmpty()) {
            return true;
        } else {
            List<Integer> sorted = new ArrayList(hand);
            Collections.sort(sorted);
            if (sorted.size() >= 3 && ((Integer)sorted.get(0)).equals(sorted.get(1)) && ((Integer)sorted.get(1)).equals(sorted.get(2))) {
                List<Integer> newHand = new ArrayList(sorted);
                newHand.remove(0);
                newHand.remove(0);
                newHand.remove(0);
                if (this.canFormMelds(newHand)) {
                    return true;
                }
            }

            if (sorted.size() >= 3) {
                int first = (Integer)sorted.get(0);
                int type = this.getCardType(first);
                if (type <= 3) {
                    int second = first + 1;
                    int third = first + 2;
                    if (sorted.contains(second) && sorted.contains(third)) {
                        List<Integer> newHand = new ArrayList(sorted);
                        newHand.remove(first);
                        newHand.remove(second);
                        newHand.remove(third);
                        if (this.canFormMelds(newHand)) {
                            return true;
                        }
                    }
                }
            }

            return false;
        }
    }

    private Set<Integer> getReasonableHuCards(List<Integer> hand) {
        Set<Integer> possibleCards = new HashSet();

        for(int card : hand) {
            possibleCards.add(card);
            int type = this.getCardType(card);
            if (type <= 3) {
                int value = this.getCardValue(card);
                if (value > 1) {
                    possibleCards.add(card - 1);
                }

                if (value < 9) {
                    possibleCards.add(card + 1);
                }
            }
        }

        for(int type = 1; type <= 3; ++type) {
            for(int jiang : JIANG_PAIS) {
                possibleCards.add(type * 100 + jiang);
            }
        }

        if (possibleCards.size() > 15) {
            List<Integer> list = new ArrayList(possibleCards);
            Collections.shuffle(list);
            possibleCards = new HashSet(list.subList(0, 15));
        }

        return possibleCards;
    }

    private String getStateKey(PlayerState state) {
        List<Integer> sortedHand = new ArrayList(state.handCards);
        Collections.sort(sortedHand);
        List<String> pongKeys = new ArrayList();

        for(List<Integer> group : state.pongGroups) {
            List<Integer> sorted = new ArrayList(group);
            Collections.sort(sorted);
            pongKeys.add(sorted.toString());
        }

        Collections.sort(pongKeys);
        List<String> gangKeys = new ArrayList();

        for(List<Integer> group : state.gangGroups) {
            List<Integer> sorted = new ArrayList(group);
            Collections.sort(sorted);
            gangKeys.add(sorted.toString());
        }

        Collections.sort(gangKeys);
        List<String> chiKeys = new ArrayList();

        for(List<Integer> group : state.chiGroups) {
            List<Integer> sorted = new ArrayList(group);
            Collections.sort(sorted);
            chiKeys.add(sorted.toString());
        }

        Collections.sort(chiKeys);
        return sortedHand.toString() + "|" + pongKeys.toString() + "|" + gangKeys.toString() + "|" + chiKeys.toString();
    }

    private int getCardValue(int card) {
        return card % 10;
    }

    private int getCardType(int card) {
        return card / 100;
    }

    String formatCard(int card) {
        int type = card / 100;
        int value = card % 10;
        switch (type) {
            case 1:
                return value + "万";
            case 2:
                return value + "筒";
            case 3:
                return value + "条";
            default:
                return "字牌";
        }
    }

    private String formatCards(List<Integer> cards) {
        return (String)cards.stream().map(this::formatCard).collect(Collectors.joining(" "));
    }

    static {
        Set<Integer> jiangSet = new HashSet();
        jiangSet.add(2);
        jiangSet.add(5);
        jiangSet.add(8);
        JIANG_PAIS = Collections.unmodifiableSet(jiangSet);
    }

    private static class DiscardResult {
        double score;
        List<Integer> tingCards;
        int searchDepth;

        DiscardResult(double score, List<Integer> tingCards, int searchDepth) {
            this.score = score;
            this.tingCards = tingCards;
            this.searchDepth = searchDepth;
        }
    }
}
