package taurus.util;

import java.util.*;
import java.util.stream.Collectors;

public class ChangshaMahjongAI {

    // 长沙麻将特殊规则：二五八将
    private static final Set<Integer> JIANG_PAIS;

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

    // 缓存优化
    private Map<String, Double> scoreCache = new HashMap<>();
    private Map<String, List<Integer>> tingCache = new HashMap<>();

    public static void main(String[] args) {
        // 测试手牌：应该打七万听二筒

        List<Integer> handCards = Arrays.asList(
                208, 208, 208, 203, 201, 107, 107,
                106, 108, 105,
                105, 101, 101, 101
        );

        ChangshaMahjongAI ai = new ChangshaMahjongAI();
        int bestDiscard = ai.findBestDiscard(handCards);
        System.out.println("111    " + bestDiscard);
    }

    /**
     * 寻找最优出牌 - 递归+回溯版本
     */
    /**
     * 寻找最优出牌 - 递归+回溯版本
     * @return 推荐打出的牌（数字编码，如106）
     */
    public int findBestDiscard(List<Integer> handCards) {
        System.out.println("=== 长沙麻将AI出牌分析（递归+回溯）===");
        System.out.println("当前手牌(" + handCards.size() + "张): " + formatCards(handCards));

        long startTime = System.currentTimeMillis();

        // 使用递归+回溯评估每张牌
        Map<Integer, DiscardResult> results = new HashMap<>();
        Set<Integer> candidateCards = new HashSet<>(handCards);

        for (int card : candidateCards) {
            List<Integer> newHand = removeOneCard(handCards, card);
            DiscardResult result = recursiveEvaluate(card, newHand, 0, 2, new HashSet<>());
            results.put(card, result);
            System.out.printf("打出 %s: 评分=%.3f (听牌=%d张, 深度=%d)%n",
                    formatCard(card), result.score, result.tingCards.size(), result.searchDepth);
        }

        // 找出最佳出牌
        DiscardResult bestResult = null;
        int bestCard = -1;

        for (Map.Entry<Integer, DiscardResult> entry : results.entrySet()) {
            if (bestResult == null || entry.getValue().score > bestResult.score) {
                bestResult = entry.getValue();
                bestCard = entry.getKey();
            }
        }

        long endTime = System.currentTimeMillis();
        System.out.printf("分析耗时: %dms%n", (endTime - startTime));

        if (bestCard != -1) {
            System.out.println("\n🎯 AI推荐出牌: " + formatCard(bestCard) + " (" + bestCard + ")");
            if (!bestResult.tingCards.isEmpty()) {
                System.out.println("✅ 立即听牌，可听 " + bestResult.tingCards.size() + " 张牌");
                System.out.println("听牌: " + bestResult.tingCards.stream()
                        .map(this::formatCard)
                        .collect(Collectors.joining(" ")));
            }
            System.out.println("综合评分: " + bestResult.score);
        }

        return bestCard; // 直接返回数字编码，如106
    }

    /**
     * 递归评估出牌（核心递归函数）
     */
    private DiscardResult recursiveEvaluate(int discardedCard, List<Integer> currentHand, int depth, int maxDepth, Set<String> visited) {
        // 生成状态key用于去重
        String stateKey = generateStateKey(currentHand, depth);
        if (visited.contains(stateKey)) {
            return new DiscardResult(0, new ArrayList<>(), depth);
        }
        visited.add(stateKey);

        // 基础评分：当前手牌结构 + 听牌检查
        double baseScore = evaluateCurrentState(currentHand);

        // 终止条件
        if (depth >= maxDepth) {
            return new DiscardResult(baseScore, checkTingCards(currentHand), depth);
        }

        // 递归探索未来可能性
        double futureScore = 0.0;
        int validPaths = 0;
        int maxSearchDepth = depth;

        // 获取合理的摸牌候选（限制数量以提升性能）
        List<Integer> drawCandidates = getSmartDrawCandidates(currentHand);

        for (int drawCard : drawCandidates) {
            if (validPaths >= 4) break; // 限制分支数量

            List<Integer> newHandAfterDraw = new ArrayList<>(currentHand);
            newHandAfterDraw.add(drawCard);
            Collections.sort(newHandAfterDraw);

            // 递归评估下一步出牌
            double bestFuture = Double.NEGATIVE_INFINITY;
            int pathDepth = depth;

            Set<Integer> nextDiscardCandidates = getSmartDiscardCandidates(newHandAfterDraw);
            for (int nextDiscard : nextDiscardCandidates) {
                List<Integer> nextHand = removeOneCard(newHandAfterDraw, nextDiscard);

                DiscardResult nextResult = recursiveEvaluate(nextDiscard, nextHand, depth + 1, maxDepth, visited);
                bestFuture = Math.max(bestFuture, nextResult.score);
                pathDepth = Math.max(pathDepth, nextResult.searchDepth);
            }

            if (bestFuture > Double.NEGATIVE_INFINITY) {
                futureScore += bestFuture;
                validPaths++;
                maxSearchDepth = Math.max(maxSearchDepth, pathDepth);
            }
        }

        // 计算未来期望分数（衰减）
        double futureAvg = validPaths > 0 ? futureScore / validPaths : 0;
        double decayFactor = Math.pow(0.6, depth); // 深度衰减
        double totalScore = baseScore + futureAvg * decayFactor;

        return new DiscardResult(totalScore, checkTingCards(currentHand), maxSearchDepth);
    }

    /**
     * 评估当前状态
     */
    private double evaluateCurrentState(List<Integer> hand) {
        String handKey = getHandKey(hand);
        if (scoreCache.containsKey(handKey)) {
            return scoreCache.get(handKey);
        }

        double score = 0.0;

        // 1. 听牌状态（最高权重）
        List<Integer> tingCards = checkTingCards(hand);
        if (!tingCards.isEmpty()) {
            score += 15.0 + tingCards.size() * 2.0;
        }

        // 2. 刻子和对子
        Map<Integer, Integer> countMap = new HashMap<>();
        for (int card : hand) {
            countMap.put(card, countMap.getOrDefault(card, 0) + 1);
        }

        for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
            int count = entry.getValue();
            int cardValue = getCardValue(entry.getKey());

            if (count >= 3) {
                score += 3.0; // 刻子
            } else if (count == 2) {
                score += 1.5; // 对子
                if (JIANG_PAIS.contains(cardValue)) {
                    score += 1.0; // 二五八将对子
                }
            }
        }

        // 3. 顺子
        score += findSequences(hand) * 2.5;

        // 4. 搭子
        score += countDazi(hand) * 1.0;

        scoreCache.put(handKey, score);
        return score;
    }

    /**
     * 检查听牌情况（使用回溯）
     */
    private List<Integer> checkTingCards(List<Integer> hand) {
        String handKey = getHandKey(hand);
        if (tingCache.containsKey(handKey)) {
            return tingCache.get(handKey);
        }

        List<Integer> tingCards = new ArrayList<>();
        if (hand.size() % 3 != 1) {
            tingCache.put(handKey, tingCards);
            return tingCards;
        }

        // 只检查合理的胡牌候选
        Set<Integer> possibleHuCards = getReasonableHuCards(hand);

        for (int huCard : possibleHuCards) {
            List<Integer> testHand = new ArrayList<>(hand);
            testHand.add(huCard);
            if (backtrackCanHu(testHand)) {
                tingCards.add(huCard);
            }
        }

        tingCache.put(handKey, tingCards);
        return tingCards;
    }

    /**
     * 回溯法检查胡牌
     */
    private boolean backtrackCanHu(List<Integer> hand) {
        if (hand.size() != 14) return false;

        List<Integer> sortedHand = new ArrayList<>(hand);
        Collections.sort(sortedHand);
        return backtrackHuSearch(sortedHand, false, 0);
    }

    /**
     * 回溯搜索胡牌组合
     */
    private boolean backtrackHuSearch(List<Integer> hand, boolean hasJiang, int startIndex) {
        if (hand.isEmpty()) return true;

        if (!hasJiang) {
            // 寻找将牌
            for (int i = startIndex; i < hand.size() - 1; i++) {
                if (hand.get(i).equals(hand.get(i + 1))) {
                    int cardValue = getCardValue(hand.get(i));
                    if (JIANG_PAIS.contains(cardValue)) {
                        List<Integer> newHand = new ArrayList<>(hand);
                        newHand.remove(i);
                        newHand.remove(i);
                        if (backtrackHuSearch(newHand, true, 0)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        // 从startIndex开始检查
        for (int i = startIndex; i < hand.size(); i++) {
            // 检查刻子
            if (i <= hand.size() - 3 &&
                    hand.get(i).equals(hand.get(i + 1)) &&
                    hand.get(i + 1).equals(hand.get(i + 2))) {
                List<Integer> newHand = new ArrayList<>(hand);
                newHand.remove(i);
                newHand.remove(i);
                newHand.remove(i);
                if (backtrackHuSearch(newHand, true, i)) {
                    return true;
                }
            }

            // 检查顺子
            int currentCard = hand.get(i);
            if (getCardType(currentCard) <= 3) {
                int nextCard1 = currentCard + 1;
                int nextCard2 = currentCard + 2;

                if (hand.contains(nextCard1) && hand.contains(nextCard2)) {
                    List<Integer> newHand = new ArrayList<>(hand);
                    newHand.remove((Integer) currentCard);
                    newHand.remove((Integer) nextCard1);
                    newHand.remove((Integer) nextCard2);
                    if (backtrackHuSearch(newHand, true, i)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * 智能获取摸牌候选
     */
    private List<Integer> getSmartDrawCandidates(List<Integer> hand) {
        Set<Integer> allCards = generateAllCards();
        allCards.removeAll(hand);
        List<Integer> candidates = new ArrayList<>(allCards);

        // 限制数量并随机选择（模拟真实概率）
        Collections.shuffle(candidates);
        return candidates.subList(0, Math.min(6, candidates.size()));
    }

    /**
     * 智能获取出牌候选
     */
    private Set<Integer> getSmartDiscardCandidates(List<Integer> hand) {
        Set<Integer> candidates = new HashSet<>();
        Map<Integer, Integer> countMap = new HashMap<>();

        for (int card : hand) {
            countMap.put(card, countMap.getOrDefault(card, 0) + 1);
        }

        // 优先考虑：孤张、边张、出现次数少的牌
        for (int card : hand) {
            int count = countMap.get(card);
            int value = getCardValue(card);

            if (count == 1 && isLonelyCard(card, hand)) {
                candidates.add(card);
            } else if (value == 1 || value == 9) {
                candidates.add(card);
            } else if (count == 1) {
                candidates.add(card);
            }
        }

        // 如果候选太少，加入一些其他牌
        if (candidates.size() < 3) {
            for (int card : hand) {
                if (!candidates.contains(card)) {
                    candidates.add(card);
                    if (candidates.size() >= 5) break;
                }
            }
        }

        return candidates;
    }

    /**
     * 获取合理的胡牌候选
     */
    private Set<Integer> getReasonableHuCards(List<Integer> hand) {
        Set<Integer> possibleCards = new HashSet<>();
        Map<Integer, Integer> countMap = new HashMap<>();

        for (int card : hand) {
            countMap.put(card, countMap.getOrDefault(card, 0) + 1);
        }

        // 基于现有牌型推测可能胡的牌
        for (int card : hand) {
            possibleCards.add(card); // 现有的牌都可能胡

            if (getCardType(card) <= 3) {
                int value = getCardValue(card);
                if (value > 1) possibleCards.add(card - 1);
                if (value < 9) possibleCards.add(card + 1);
            }
        }

        return possibleCards;
    }

    // 工具方法
    private String generateStateKey(List<Integer> hand, int depth) {
        List<Integer> sorted = new ArrayList<>(hand);
        Collections.sort(sorted);
        return sorted.toString() + "|" + depth;
    }

    private String getHandKey(List<Integer> hand) {
        List<Integer> sorted = new ArrayList<>(hand);
        Collections.sort(sorted);
        return sorted.toString();
    }

    private boolean isLonelyCard(int card, List<Integer> hand) {
        if (getCardType(card) == 4) return true;
        int type = getCardType(card);
        int value = getCardValue(card);

        for (int other : hand) {
            if (other == card) continue;
            if (getCardType(other) == type && Math.abs(getCardValue(other) - value) <= 2) {
                return false;
            }
        }
        return true;
    }

    private int findSequences(List<Integer> hand) {
        int count = 0;
        List<Integer> temp = new ArrayList<>(hand);
        temp.removeIf(c -> getCardType(c) == 4);
        Collections.sort(temp);

        for (int i = 0; i <= temp.size() - 3; i++) {
            if (temp.get(i) + 1 == temp.get(i + 1) && temp.get(i) + 2 == temp.get(i + 2)) {
                count++;
                i += 2;
            }
        }
        return count;
    }

    private int countDazi(List<Integer> hand) {
        int count = 0;
        Set<Integer> unique = new HashSet<>(hand);
        Map<Integer, Integer> freq = new HashMap<>();
        for (int card : hand) freq.put(card, freq.getOrDefault(card, 0) + 1);

        for (int card : unique) {
            if (getCardType(card) == 4) continue;
            if (freq.get(card) >= 2) count++;
            if (unique.contains(card + 1)) count++;
        }
        return count;
    }

    private Set<Integer> generateAllCards() {
        Set<Integer> cards = new HashSet<>();
        for (int type = 1; type <= 3; type++) {
            for (int value = 1; value <= 9; value++) {
                cards.add(type * 100 + value);
            }
        }
        return cards;
    }

    private List<Integer> removeOneCard(List<Integer> hand, int card) {
        List<Integer> newHand = new ArrayList<>(hand);
        newHand.remove((Integer) card);
        return newHand;
    }

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

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

    private 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 cards.stream().map(this::formatCard).collect(Collectors.joining(" "));
    }

    /**
     * 出牌结果类
     */
    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;
        }
    }
}