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 class PlayerState {
        public List<Integer> handCards = new ArrayList<>();
        public List<List<Integer>> pongGroups = new ArrayList<>();
        public List<List<Integer>> gangGroups = new ArrayList<>();
        public List<List<Integer>> chiGroups = new ArrayList<>();
        public boolean isZhuang = false;

        public void addPongGroup(List<Integer> group) {
            pongGroups.add(new ArrayList<>(group));
        }

        public void addGangGroup(List<Integer> group) {
            gangGroups.add(new ArrayList<>(group));
        }

        public void addChiGroup(List<Integer> group) {
            chiGroups.add(new ArrayList<>(group));
        }

        public int getPongGroupCount() {
            return pongGroups.size();
        }

        public int getGangGroupCount() {
            return gangGroups.size();
        }

        public int getChiGroupCount() {
            return chiGroups.size();
        }

        public int getTotalGroupCount() {
            return getPongGroupCount() + getGangGroupCount() + getChiGroupCount();
        }
    }

    public static void main(String[] args) {
        System.out.println("=== 长沙麻将AI测试（含碰杠吃牌） ===");
        ChangshaMahjongAI ai = new ChangshaMahjongAI();

        // 测试1：普通14张牌
        System.out.println("\n=== 测试1：普通14张牌 ===");
        PlayerState state1 = new PlayerState();
        state1.handCards = Arrays.asList(204, 206, 205, 205, 203, 109, 108, 107, 107, 106, 103, 103, 102, 101);
        System.out.println("手牌: " + ai.formatCards(state1.handCards));
        int discard1 = ai.findBestDiscard(state1);
        System.out.println("推荐出牌: " + ai.formatCard(discard1));
        //要打6筒  ======================================================================================================
    }

    /**
     * 主方法：根据完整玩家状态推荐出牌
     */
    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();

        if (pongGroups > 0) {
            System.out.println("碰牌组数: " + pongGroups);
        }
        if (gangGroups > 0) {
            System.out.println("杠牌组数: " + gangGroups);
        }
        if (chiGroups > 0) {
            System.out.println("吃牌组数: " + 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)
                        .sorted()
                        .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) {
        String stateKey = getStateKey(state);
        if (scoreCache.containsKey(stateKey)) {
            List<Integer> tingCards = checkTingCardsWithState(state);
            return new DiscardResult(scoreCache.get(stateKey), tingCards, 0);
        }

        double totalScore = 0.0;

        // 1. 核心：评估保留对子的价值（特别是258将牌）
        totalScore += evaluatePairRetention(state.handCards);

        // 2. 评估花色分布的合理性
        totalScore += evaluateSuitDistribution(state.handCards);

        // 3. 评估牌型完整性
        totalScore += evaluateHandStructure(state.handCards);

        // 4. 检查听牌
        List<Integer> tingCards = checkTingCardsWithState(state);
        if (!tingCards.isEmpty()) {
            totalScore += 50.0; // 听牌大幅加分
        }

        // 5. 安全评估 - 打出牌的安全性
        totalScore += evaluateSafety(discardedCard);

        // 6. 已碰杠吃牌的加分
        totalScore += evaluateGroupBonus(state);

        scoreCache.put(stateKey, totalScore);
        return new DiscardResult(totalScore, tingCards, 0);
    }

    /**
     * 评估保留对子的价值 - 新方法，重点关注
     */
    private double evaluatePairRetention(List<Integer> hand) {
        double score = 0.0;

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

        // 检查258将牌的对子
        boolean has258Pair = false;
        for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
            if (entry.getValue() >= 2) {
                int cardValue = getCardValue(entry.getKey());
                if (JIANG_PAIS.contains(cardValue)) {
                    score += 30.0; // 258将牌对子非常重要
                    has258Pair = true;
                } else {
                    score += 10.0; // 普通对子也有价值
                }
            }
        }

        // 如果没有258对子，需要更加注意保留258将牌
        if (!has258Pair) {
            for (int card : hand) {
                if (JIANG_PAIS.contains(getCardValue(card))) {
                    score += 5.0; // 单张258将牌也要保留
                }
            }
        }

        return score;
    }

    /**
     * 评估花色分布 - 新方法
     */
    private double evaluateSuitDistribution(List<Integer> hand) {
        double score = 0.0;

        // 按花色分组
        Map<Integer, List<Integer>> suitGroups = new HashMap<>();
        for (int card : hand) {
            int suit = getCardType(card);
            suitGroups.putIfAbsent(suit, new ArrayList<>());
            suitGroups.get(suit).add(card);
        }

        // 评估每种花色的牌数
        for (Map.Entry<Integer, List<Integer>> entry : suitGroups.entrySet()) {
            int suit = entry.getKey();
            int count = entry.getValue().size();

            // 万筒条花色
            if (suit == 1 || suit == 2 || suit == 3) {
                if (count >= 8) {
                    score += 20.0; // 清一色潜力
                } else if (count >= 5) {
                    score += 10.0; // 一色牌较多
                } else if (count == 1) {
                    score -= 5.0; // 单张花色，鼓励打掉
                } else if (count == 2) {
                    score -= 2.0; // 双张花色，酌情考虑
                }
            }
        }

        return score;
    }

    /**
     * 检查听牌情况
     */
    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 double evaluateHandStructure(List<Integer> hand) {
        if (hand.isEmpty()) {
            return 0.0;
        }

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

        // 1. 顺子检查
        score += findSequences(hand) * 15.0;

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

        for (int count : countMap.values()) {
            if (count >= 3) {
                score += 20.0; // 刻子
            } else if (count == 2) {
                score += 8.0; // 对子
            }
        }

        // 3. 搭子检查
        score += countDazi(hand) * 5.0;

        // 4. 孤立张检查
        score += evaluateIsolatedCards(hand);

        return score;
    }

    /**
     * 孤立张评估
     */
    private double evaluateIsolatedCards(List<Integer> hand) {
        double score = 0.0;

        // 按花色分组
        Map<Integer, Set<Integer>> suitValues = new HashMap<>();
        for (int card : hand) {
            int suit = getCardType(card);
            int value = getCardValue(card);
            suitValues.putIfAbsent(suit, new HashSet<>());
            suitValues.get(suit).add(value);
        }

        // 检查每张牌是否为孤立张
        for (int card : hand) {
            int suit = getCardType(card);
            int value = getCardValue(card);

            if (suit > 3) continue; // 字牌暂不考虑

            Set<Integer> values = suitValues.get(suit);
            boolean isIsolated = true;

            // 检查相邻牌
            for (int neighbor : values) {
                if (neighbor != value && Math.abs(neighbor - value) <= 2) {
                    isIsolated = false;
                    break;
                }
            }

            if (isIsolated) {
                // 孤立张，根据位置扣分
                if (value == 1 || value == 9) {
                    score -= 3.0; // 边张孤立
                } else if (JIANG_PAIS.contains(value)) {
                    score -= 8.0; // 258将牌孤立，鼓励打掉
                } else {
                    score -= 5.0; // 中间张孤立
                }
            }
        }

        return score;
    }

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

        if (type > 3) {
            return -10.0; // 字牌风险高
        }

        if (value == 1 || value == 9) {
            return -3.0; // 边张风险
        }

        if (JIANG_PAIS.contains(value)) {
            return -15.0; // 258将牌风险很高
        }

        // 中间张相对安全
        return 2.0;
    }

    /**
     * 胡牌检查
     */
    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 firstCard = sorted.get(0);
            int firstType = getCardType(firstCard);
            int firstValue = getCardValue(firstCard);

            if (firstType <= 3 && firstValue <= 7) {
                int secondCard = firstCard + 1;
                int thirdCard = firstCard + 2;

                if (sorted.contains(secondCard) && sorted.contains(thirdCard)) {
                    List<Integer> newHand = new ArrayList<>(sorted);
                    newHand.remove((Integer) firstCard);
                    newHand.remove((Integer) secondCard);
                    newHand.remove((Integer) thirdCard);
                    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 += 5.0;
                if (JIANG_PAIS.contains(value)) {
                    bonus += 3.0;
                }
            }
        }

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

        // 吃牌加分
        for (List<Integer> chiGroup : state.chiGroups) {
            if (!chiGroup.isEmpty()) {
                bonus += 2.0;
            }
        }

        return bonus;
    }

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

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

        // 所有258将牌
        for (int type = 1; type <= 3; type++) {
            for (int jiang : JIANG_PAIS) {
                possibleCards.add(type * 100 + jiang);
            }
        }

        // 限制数量
        if (possibleCards.size() > 20) {
            List<Integer> list = new ArrayList<>(possibleCards);
            Collections.shuffle(list);
            possibleCards = new HashSet<>(list.subList(0, 20));
        }

        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()
                .sorted()
                .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;
        }
    }
}