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) {
        System.out.println("=== 长沙麻将AI测试（含碰杠吃牌） ===");
        ChangshaMahjongAI ai = new ChangshaMahjongAI();

//        // 测试1：普通手牌
        System.out.println("\n=== 测试1：普通14张牌 ===");
        PlayerState state1 = new PlayerState();
        state1.handCards = Arrays.asList(208,208,208,207,205,205,203,203,102,102,102);
//        state1.addChiGroup(Arrays.asList(207,208,209));
        System.out.println("最终手牌---"+state1.handCards);
        int discard1 = ai.findBestDiscard(state1);
        System.out.println("推荐出牌: " + ai.formatCard(discard1));

        // 测试2：有碰牌和吃牌
//        System.out.println("\n=== 测试2：有碰牌和吃牌 ===");
//        PlayerState state2 = new PlayerState();
//        state2.handCards = Arrays.asList(209, 209, 207, 206, 205,204,102,106);

//        // 碰了222万
//        state2.addPongGroup(Arrays.asList(102, 102, 102));
//        // 吃了345条
//        state2.addChiGroup(Arrays.asList(306, 307, 308));

//        int discard2 = ai.findBestDiscard(state2);
//        System.out.println("推荐出牌: " + ai.formatCard(discard2));
//
//        // 测试3：混合情况
//        System.out.println("\n=== 测试3：碰+杠+吃混合 ===");
//        PlayerState state3 = new PlayerState();
//        state3.handCards = Arrays.asList(208, 208);
//
//        // 碰了555筒
//        state3.addPongGroup(Arrays.asList(205, 205, 205));
//        // 杠了888万
//        state3.addGangGroup(Arrays.asList(108, 108, 108, 108));
//        // 吃了123条
//        state3.addChiGroup(Arrays.asList(301, 302, 303));
//
//        int discard3 = ai.findBestDiscard(state3);
//        System.out.println("推荐出牌: " + ai.formatCard(discard3));
    }

    /**
     * 主方法：根据完整玩家状态推荐出牌
     */
    public int findBestDiscard(PlayerState state) {
        System.out.println("\n=== 长沙麻将AI出牌分析 ===");
        System.out.println("手牌(" + state.handCards.size() + "张): " + formatCards(state.handCards));

        int pongGroups = state.getPongGroupCount();
        int gangGroups = state.getGangGroupCount();
        int chiGroups = state.getChiGroupCount();

        System.out.println("碰牌组数: " + pongGroups +
                "，杠牌组数: " + gangGroups +
                "，吃牌组数: " + chiGroups);

        // 清空缓存
        scoreCache.clear();
        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("只剩一张牌，必须出: " + formatCard(onlyCard));
            return onlyCard;
        }

        for (int card : uniqueCards) {
            // 模拟打出一张牌后的状态
            PlayerState newState = simulateDiscard(state, card);
            DiscardResult result = evaluateDiscardWithState(card, newState);
            results.put(card, result);

            System.out.printf("打出 %s: 评分=%.3f (听牌=%d张)%n",
                    formatCard(card), result.score, result.tingCards.size());
        }

        // 找出最佳出牌
        int bestCard = -1;
        double bestScore = Double.NEGATIVE_INFINITY;

        for (Map.Entry<Integer, DiscardResult> entry : results.entrySet()) {
            if (entry.getValue().score > bestScore) {
                bestScore = entry.getValue().score;
                bestCard = 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🎯 AI推荐出牌: " + formatCard(bestCard));
            if (!bestResult.tingCards.isEmpty()) {
                System.out.println("✅ 听牌，可听 " + bestResult.tingCards.size() + " 张牌:");
                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((Integer) 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 state) {
        // 1. 计算基础牌型分
        double baseScore = evaluateHandStructure(state);

        // 2. 检查听牌
        List<Integer> tingCards = checkTingCardsWithState(state);

        // 3. 牌效率评估
        double efficiencyScore = evaluateEfficiency(state.handCards);

        // 4. 安全评估
        double safetyScore = evaluateSafety(discardedCard);

        // 5. 已碰杠吃牌的加分
        double groupBonus = evaluateGroupBonus(state);

        double totalScore = baseScore + efficiencyScore + safetyScore + groupBonus;

        // 听牌额外加分（权重更高）
        if (!tingCards.isEmpty()) {
            totalScore += 20.0 + tingCards.size() * 2.5;

            // 如果是二五八将的听牌，额外加分
            for (int tingCard : tingCards) {
                if (JIANG_PAIS.contains(getCardValue(tingCard))) {
                    totalScore += 5.0;
                }
            }
        }

        return new DiscardResult(totalScore, tingCards, 0);
    }

    /**
     * 检查听牌情况
     */
    private List<Integer> checkTingCardsWithState(PlayerState state) {
        String stateKey = getStateKey(state);
        if (tingCache.containsKey(stateKey)) {
            return tingCache.get(stateKey);
        }

        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;

        // 听牌时手牌数量应为：14 - 已使用的牌数 - 1（要胡的牌）
        int expectedHandCount = 14 - usedCardsCount - 1;

        if (state.handCards.size() != expectedHandCount) {
            tingCache.put(stateKey, tingCards);
            return tingCards;
        }

        // 获取合理的胡牌候选
        Set<Integer> possibleHuCards = getReasonableHuCards(state.handCards);

        for (int huCard : possibleHuCards) {
            if (canHuWithState(state, huCard)) {
                tingCards.add(huCard);
            }
        }

        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;
        }

        List<Integer> remainingHand = new ArrayList<>(state.handCards);
        remainingHand.add(huCard);
        Collections.sort(remainingHand);

        return canHuWith258(remainingHand);
    }

    /**
     * 检查手牌是否能胡牌（考虑258将）
     */
    private boolean canHuWith258(List<Integer> hand) {
        if (hand.size() != 2 && hand.size() != 5 && hand.size() != 8 && hand.size() != 11 && hand.size() != 14) {
            return false;
        }

        List<Integer> sortedHand = new ArrayList<>(hand);
        Collections.sort(sortedHand);

        // 尝试所有可能的将牌
        for (int i = 0; i < sortedHand.size() - 1; i++) {
            if (sortedHand.get(i).equals(sortedHand.get(i + 1))) {
                int cardValue = getCardValue(sortedHand.get(i));
                if (JIANG_PAIS.contains(cardValue)) {
                    List<Integer> tempHand = new ArrayList<>(sortedHand);
                    tempHand.remove(i);
                    tempHand.remove(i);

                    if (canFormMelds(tempHand)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * 检查是否能组成全部顺子/刻子
     */
    private boolean canFormMelds(List<Integer> hand) {
        if (hand.isEmpty()) {
            return true;
        }

        List<Integer> sorted = new ArrayList<>(hand);
        Collections.sort(sorted);

        // 尝试刻子
        if (sorted.size() >= 3 &&
                sorted.get(0).equals(sorted.get(1)) &&
                sorted.get(1).equals(sorted.get(2))) {
            List<Integer> newHand = new ArrayList<>(sorted);
            newHand.remove(0);
            newHand.remove(0);
            newHand.remove(0);
            if (canFormMelds(newHand)) {
                return true;
            }
        }

        // 尝试顺子
        if (sorted.size() >= 3) {
            int first = sorted.get(0);
            int type = 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((Integer) first);
                    newHand.remove((Integer) second);
                    newHand.remove((Integer) third);
                    if (canFormMelds(newHand)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * 评估碰杠吃牌的加分
     */
    private double evaluateGroupBonus(PlayerState state) {
        double bonus = 0.0;

        // 碰牌加分
        for (List<Integer> pongGroup : state.pongGroups) {
            if (!pongGroup.isEmpty()) {
                int card = pongGroup.get(0);
                int value = getCardValue(card);
                bonus += 3.0;
                if (JIANG_PAIS.contains(value)) {
                    bonus += 2.0;
                }
            }
        }

        // 杠牌加分
        for (List<Integer> gangGroup : state.gangGroups) {
            if (!gangGroup.isEmpty()) {
                int card = gangGroup.get(0);
                int value = getCardValue(card);
                bonus += 8.0;
                if (JIANG_PAIS.contains(value)) {
                    bonus += 2.0;
                }
            }
        }

        // 吃牌加分（比碰牌分数低）
        for (List<Integer> chiGroup : state.chiGroups) {
            if (!chiGroup.isEmpty()) {
                bonus += 1.5;
                // 检查吃的牌是否包含258
                for (int card : chiGroup) {
                    if (JIANG_PAIS.contains(getCardValue(card))) {
                        bonus += 0.5;
                        break;
                    }
                }
            }
        }

        // 总组数越多，越接近胡牌
        int totalGroups = state.getTotalGroupCount();
        if (totalGroups >= 3) {
            bonus += 10.0;
        }

        return bonus;
    }

    /**
     * 手牌结构评估
     */
    private double evaluateHandStructure(PlayerState state) {
        if (state.handCards.isEmpty()) {
            return 0.0;
        }

        double score = 0.0;
        Map<Integer, Integer> countMap = new HashMap<>();

        for (int card : state.handCards) {
            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 += 5.0;
                if (JIANG_PAIS.contains(cardValue)) {
                    score += 3.0;
                }
            } else if (count == 2) {
                score += 2.0;
                if (JIANG_PAIS.contains(cardValue)) {
                    score += 2.0;
                }
            }
        }

        // 顺子
        score += findSequences(state.handCards) * 4.0;

        // 搭子
        score += countDazi(state.handCards) * 2.0;

        // 手牌数量少时加分
        if (state.handCards.size() <= 4) {
            score += (5 - state.handCards.size()) * 3.0;
        }

        return score;
    }

    /**
     * 牌效率评估
     */
    private double evaluateEfficiency(List<Integer> hand) {
        if (hand.isEmpty()) {
            return 0.0;
        }

        double score = 0.0;
        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 type = getCardType(card);
            int value = getCardValue(card);

            if (count == 1) {
                if (value >= 2 && value <= 8) {
                    score += 0.8;
                    if (JIANG_PAIS.contains(value)) {
                        score += 1.2;
                    }
                } else {
                    score += 0.3;
                }

                // 检查是否有相邻牌
                if (type <= 3) {
                    boolean hasNeighbor = false;
                    for (int other : hand) {
                        if (other != card && getCardType(other) == type &&
                                Math.abs(getCardValue(other) - value) <= 2) {
                            hasNeighbor = true;
                            break;
                        }
                    }
                    if (hasNeighbor) {
                        score += 0.5;
                    }
                }
            }
        }

        return score;
    }

    /**
     * 安全评估
     */
    private double evaluateSafety(int discardedCard) {
        int value = getCardValue(discardedCard);
        int type = getCardType(discardedCard);

        if (type > 3) {
            return -1.0;
        }

        if (value == 1 || value == 9) {
            return -0.5;
        }

        if (JIANG_PAIS.contains(value)) {
            return -2.0;
        }

        return 0.0;
    }

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

        // 现有牌和相邻牌
        for (int card : hand) {
            possibleCards.add(card);
            int type = getCardType(card);
            if (type <= 3) {
                int value = getCardValue(card);
                if (value > 1) possibleCards.add(card - 1);
                if (value < 9) possibleCards.add(card + 1);
            }
        }

        // 所有258牌
        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;
    }

    /**
     * 生成状态key
     */
    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 findSequences(List<Integer> hand) {
        int count = 0;
        List<Integer> temp = new ArrayList<>(hand);
        temp.removeIf(c -> getCardType(c) > 3);
        Collections.sort(temp);

        boolean[] used = new boolean[temp.size()];

        for (int i = 0; i <= temp.size() - 3; i++) {
            if (used[i]) continue;

            int card1 = temp.get(i);
            int card2 = card1 + 1;
            int card3 = card1 + 2;

            int idx2 = -1, idx3 = -1;
            for (int j = i + 1; j < temp.size(); j++) {
                if (!used[j]) {
                    if (temp.get(j) == card2) idx2 = j;
                    if (temp.get(j) == card3) idx3 = j;
                }
            }

            if (idx2 != -1 && idx3 != -1) {
                count++;
                used[i] = true;
                used[idx2] = true;
                used[idx3] = true;
            }
        }

        return count;
    }

    /**
     * 统计搭子数量
     */
    private int countDazi(List<Integer> hand) {
        int count = 0;
        Set<Integer> unique = new HashSet<>(hand);

        for (int card : unique) {
            if (getCardType(card) > 3) continue;

            // 对子
            if (Collections.frequency(hand, card) >= 2) {
                count++;
            }

            // 连张
            if (unique.contains(card + 1)) {
                count++;
            }
        }

        return count;
    }

    /**
     * 获取牌值
     */
    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 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;
        }
    }
}