package taurus.util;

import java.util.*;

public class TingPaiChecker {

    // 长沙麻将特殊规则：二五八将
    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);
    }

    /**
     * 判断手牌是否听牌（主要入口方法）
     * @param handCards 手牌列表（13张或14张）
     * @return TingResult 听牌结果对象
     */
    /**
     * 判断手牌是否听牌（主要入口方法）
     */
    public static TingResult checkTingPai(List<Integer> handCards) {
        TingResult result = new TingResult();

        if (handCards == null || handCards.isEmpty()) {
            result.setMessage("手牌为空");
            return result;
        }

        int handSize = handCards.size();
        System.out.println("手牌数量: " + handSize + "张");
        System.out.println("手牌: " + convertToReadable(handCards));

        // 1. 检查手牌数量是否合法
        if (handSize != 13 && handSize != 14) {
            result.setMessage("手牌数量必须是13张（未听牌）或14张（已胡牌）");
            return result;
        }

        // 2. 如果手牌14张，先检查是否已经胡牌
        if (handSize == 14) {
            HuResult huResult = checkAllHuTypes(handCards);
            if (huResult.canHu) {
                result.setAlreadyHu(true);
                result.setMessage("已胡牌 - " + huResult.huType);
                result.setHuType(huResult.huType);
                System.out.println("✓ 已胡牌 (" + huResult.huType + ")");
                return result;
            }
        }

        // 3. 检查是否听牌
        List<TingCardInfo> tingCards = findTingCardsWithType(handCards);

        if (!tingCards.isEmpty()) {
            result.setTingPai(true);
            result.setTingCardsInfo(tingCards);
            result.setMessage("听牌，可听" + tingCards.size() + "张牌");
            System.out.println("✓ 听牌");
            System.out.println("听牌数: " + tingCards.size() + "张");

            // 按胡牌类型分组显示
            Map<String, List<TingCardInfo>> groupedByType = new HashMap<>();
            for (TingCardInfo info : tingCards) {
                groupedByType.computeIfAbsent(info.huType, k -> new ArrayList<>()).add(info);
            }

            for (Map.Entry<String, List<TingCardInfo>> entry : groupedByType.entrySet()) {
                System.out.print("  " + entry.getKey() + "听牌: ");
                List<String> cardStrs = new ArrayList<>();
                for (TingCardInfo info : entry.getValue()) {
                    cardStrs.add(formatCard(info.card));
                }
                System.out.println(String.join(" ", cardStrs));
            }
        } else {
            result.setTingPai(false);
            result.setMessage("未听牌");
            System.out.println("✗ 未听牌");
        }

        return result;
    }
    /**
     * 检查所有胡牌类型
     */
    /**
     * 检查所有胡牌类型
     */
    private static HuResult checkAllHuTypes(List<Integer> handCards) {
        // 按优先级检查
        HuResult pengPengHu = checkPengPengHu(handCards);
        if (pengPengHu.canHu) {
            return pengPengHu;
        }

        HuResult qingYiSe = checkQingYiSe(handCards);
        if (qingYiSe.canHu) {
            return qingYiSe;
        }

        HuResult normalHu = checkNormalHuWith258(handCards);
        if (normalHu.canHu) {
            return normalHu;
        }

        HuResult qiDui = checkQiDui(handCards);
        if (qiDui.canHu) {
            return qiDui;
        }

        return new HuResult(false, "");
    }
    /**
     * 检查碰碰胡（不需要258将）
     * 碰碰胡规则：4组刻子+1对将（对子可以是任意牌）
     */
    /**
     * 修复的碰碰胡检查
     */
    private static HuResult checkPengPengHu(List<Integer> handCards) {
        if (handCards.size() != 14) return new HuResult(false, "");

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

        // 统计每种牌的数量
        Map<Integer, Integer> countMap = new HashMap<>();
        for (int card : sorted) {
            countMap.put(card, countMap.getOrDefault(card, 0) + 1);
        }

        int keziCount = 0; // 刻子数量
        int duiziCount = 0; // 对子数量
        int singleCount = 0; // 单张数量

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

            if (count == 3) {
                keziCount++;
            } else if (count == 2) {
                duiziCount++;
            } else if (count == 4) {
                // 杠可以看作一个刻子
                keziCount++;
            } else if (count == 1) {
                singleCount++;
            }
        }

        // 碰碰胡条件：4组刻子 + 1对将，不能有单张
        if (keziCount == 4 && duiziCount == 1 && singleCount == 0) {
            // 检查是否清一色
            boolean isQingYiSe = checkIfQingYiSe(handCards);
            if (isQingYiSe) {
                return new HuResult(true, "清一色碰碰胡");
            }
            return new HuResult(true, "碰碰胡");
        }

        return new HuResult(false, "");
    }
    /**
     * 检查清一色（不需要258将）
     * 清一色规则：所有牌都是同一花色，可以是任意胡牌牌型
     */
    /**
     * 检查清一色
     */
    private static HuResult checkQingYiSe(List<Integer> handCards) {
        if (handCards.size() != 14) return new HuResult(false, "");

        // 检查是否清一色
        if (!checkIfQingYiSe(handCards)) {
            return new HuResult(false, "");
        }

        // 清一色只需要能胡牌即可，不需要258将
        if (canHuWithout258(handCards)) {
            return new HuResult(true, "清一色");
        }

        return new HuResult(false, "");
    }

    /**
     * 检查是否清一色
     */
    /**
     */
    private static boolean checkIfQingYiSe(List<Integer> handCards) {
        if (handCards.isEmpty()) return false;

        int firstType = getCardType(handCards.get(0));
        for (int card : handCards) {
            int type = getCardType(card);
            // 字牌不算清一色
            if (type >= 4 || type != firstType) {
                return false;
            }
        }
        return true;
    }

    /**
     * 普通胡牌（不需要258将）
     */
    private static boolean canHuWithout258(List<Integer> handCards) {
        return canHuRecursive(new ArrayList<>(handCards), false, false);
    }

    /**
     * 检查普通胡牌（必须有258将）
     */
    private static HuResult checkNormalHuWith258(List<Integer> handCards) {
        if (handCards.size() != 14) return new HuResult(false, "");

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

        // 先检查是否可能胡牌（基础条件）
        if (!canHuRecursive(sorted, false, true)) {
            return new HuResult(false, "");
        }

        return new HuResult(true, "普通胡牌");
    }

    /**
     * 检查七对子
     */
    private static HuResult checkQiDui(List<Integer> handCards) {
        if (handCards.size() != 14) return new HuResult(false, "");

        Map<Integer, Integer> countMap = new HashMap<>();
        for (int card : handCards) {
            countMap.put(card, countMap.getOrDefault(card, 0) + 1);
        }

        // 检查是否都是对子
        int duiziCount = 0;
        for (int count : countMap.values()) {
            if (count == 2) {
                duiziCount++;
            } else if (count == 4) {
                // 4张相同可以看作2个对子
                duiziCount += 2;
            } else {
                return new HuResult(false, "");
            }
        }

        if (duiziCount == 7) {
            boolean isQingYiSe = checkIfQingYiSe(handCards);
            if (isQingYiSe) {
                return new HuResult(true, "清一色七对");
            }
            return new HuResult(true, "七对子");
        }

        return new HuResult(false, "");
    }
    /**
     * 递归胡牌检查
     */
    private static boolean canHuRecursive(List<Integer> handCards, boolean hasJiang, boolean require258) {
        if (handCards.isEmpty()) {
            return hasJiang;
        }

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

        int firstCard = sorted.get(0);
        int count = Collections.frequency(sorted, firstCard);

        // 1. 尝试作为刻子
        if (count >= 3) {
            List<Integer> remaining = new ArrayList<>(sorted);
            for (int i = 0; i < 3; i++) {
                remaining.remove(Integer.valueOf(firstCard));
            }
            if (canHuRecursive(remaining, hasJiang, require258)) {
                return true;
            }
        }

        // 2. 尝试作为顺子（只有万筒条）
        int type = getCardType(firstCard);
        int value = getCardValue(firstCard);

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

            if (sorted.contains(secondCard) && sorted.contains(thirdCard)) {
                List<Integer> remaining = new ArrayList<>(sorted);
                remaining.remove(Integer.valueOf(firstCard));
                remaining.remove(Integer.valueOf(secondCard));
                remaining.remove(Integer.valueOf(thirdCard));
                if (canHuRecursive(remaining, hasJiang, require258)) {
                    return true;
                }
            }
        }

        // 3. 尝试作为将
        if (!hasJiang && count >= 2) {
            if (require258) {
                int jiangValue = getCardValue(firstCard);
                if (!JIANG_PAIS.contains(jiangValue)) {
                    return false;
                }
            }

            List<Integer> remaining = new ArrayList<>(sorted);
            remaining.remove(Integer.valueOf(firstCard));
            remaining.remove(Integer.valueOf(firstCard));
            if (canHuRecursive(remaining, true, require258)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 查找所有可能的听牌（带胡牌类型）
     */
    private static List<TingCardInfo> findTingCardsWithType(List<Integer> handCards) {
        List<TingCardInfo> tingCards = new ArrayList<>();

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

        System.out.println("候选胡牌(" + possibleHuCards.size() + "张): " +
                convertToReadable(new ArrayList<>(possibleHuCards)));

        for (int huCard : possibleHuCards) {
            List<Integer> testHand = new ArrayList<>(handCards);
            testHand.add(huCard);
            Collections.sort(testHand);

            HuResult result = checkAllHuTypes(testHand);
            if (result.canHu) {
                tingCards.add(new TingCardInfo(huCard, result.huType));
            }
        }

        return tingCards;
    }

    /**
     * 获取合理的候选胡牌（优化版）
     */
    private static Set<Integer> getReasonableHuCards(List<Integer> handCards) {
        Set<Integer> possibleCards = new HashSet<>();

        // 1. 手牌中已有的牌（优先考虑）
        for (int card : handCards) {
            possibleCards.add(card);
        }

        // 2. 手牌的相邻牌（万筒条）
        for (int card : handCards) {
            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);
            }
        }

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

        return possibleCards;
    }

    /**
     * 检查剩余牌是否能全部组成顺子或刻子
     */
    private boolean canFormAllMelds(List<Integer> cards) {
        if (cards.isEmpty()) {
            return true;
        }
        return dfsFormMelds(new ArrayList<>(cards));
    }

    private boolean dfsFormMelds(List<Integer> cards) {
        if (cards.isEmpty()) {
            return true;
        }

        Collections.sort(cards);
        int firstCard = cards.get(0);
        int count = Collections.frequency(cards, firstCard);

        // 尝试刻子
        if (count >= 3) {
            List<Integer> newCards = new ArrayList<>(cards);
            for (int i = 0; i < 3; i++) {
                newCards.remove(Integer.valueOf(firstCard));
            }
            if (dfsFormMelds(newCards)) {
                return true;
            }
        }

        // 尝试顺子
        int type = getCardType(firstCard);
        int value = getCardValue(firstCard);

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

            if (cards.contains(secondCard) && cards.contains(thirdCard)) {
                List<Integer> newCards = new ArrayList<>(cards);
                newCards.remove(Integer.valueOf(firstCard));
                newCards.remove(Integer.valueOf(secondCard));
                newCards.remove(Integer.valueOf(thirdCard));
                if (dfsFormMelds(newCards)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 获取牌值
     */
    private static int getCardValue(int card) {
        return card % 10;
    }

    /**
     * 获取牌类型
     */
    private static int getCardType(int card) {
        return card / 100;
    }

    /**
     * 格式化单张牌
     */
    private static String formatCard(int card) {
        int type = getCardType(card);
        int value = getCardValue(card);
        switch (type) {
            case 1:
                return value + "万";
            case 2:
                return value + "筒";
            case 3:
                return value + "条";
            default:
                return "字牌" + value;
        }
    }

    /**
     * 转换为可读格式
     */
    private static String convertToReadable(List<Integer> cards) {
        if (cards == null || cards.isEmpty()) {
            return "空";
        }

        List<String> cardStrs = new ArrayList<>();
        for (int card : cards) {
            cardStrs.add(formatCard(card));
        }
        return String.join(" ", cardStrs);
    }

    /**
     * 胡牌结果类
     */
    private static class HuResult {
        boolean canHu;
        String huType;

        HuResult(boolean canHu, String huType) {
            this.canHu = canHu;
            this.huType = huType;
        }
    }

    /**
     * 听牌信息类
     */
    public static class TingCardInfo {
        int card;
        String huType;

        public TingCardInfo(int card, String huType) {
            this.card = card;
            this.huType = huType;
        }

        public int getCard() {
            return card;
        }

        public String getHuType() {
            return huType;
        }
    }

    /**
     * 听牌结果类
     */
    public static class TingResult {
        private boolean isTingPai;
        private boolean isAlreadyHu;
        private String huType;
        private List<TingCardInfo> tingCardsInfo;
        private String message;

        public TingResult() {
            this.tingCardsInfo = new ArrayList<>();
        }

        public boolean isTingPai() {
            return isTingPai;
        }

        public void setTingPai(boolean tingPai) {
            isTingPai = tingPai;
        }

        public boolean isAlreadyHu() {
            return isAlreadyHu;
        }

        public void setAlreadyHu(boolean alreadyHu) {
            isAlreadyHu = alreadyHu;
        }

        public String getHuType() {
            return huType;
        }

        public void setHuType(String huType) {
            this.huType = huType;
        }

        public List<TingCardInfo> getTingCardsInfo() {
            return tingCardsInfo;
        }

        public void setTingCardsInfo(List<TingCardInfo> tingCardsInfo) {
            this.tingCardsInfo = tingCardsInfo;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("听牌分析结果:\n");
            sb.append("状态: ").append(message).append("\n");

            if (isAlreadyHu) {
                sb.append("🎉 已胡牌 (").append(huType).append(")\n");
            } else if (isTingPai) {
                sb.append("✅ 听牌状态\n");
                sb.append("可胡牌: ").append(tingCardsInfo.size()).append("张\n");

                // 按胡牌类型分组显示
                Map<String, List<TingCardInfo>> grouped = new HashMap<>();
                for (TingCardInfo info : tingCardsInfo) {
                    grouped.computeIfAbsent(info.huType, k -> new ArrayList<>()).add(info);
                }

                for (Map.Entry<String, List<TingCardInfo>> entry : grouped.entrySet()) {
                    sb.append(entry.getKey()).append(":\n");
                    // 按花色分组
                    Map<Integer, List<Integer>> typeGrouped = new HashMap<>();
                    for (TingCardInfo info : entry.getValue()) {
                        int type = info.card / 100;
                        typeGrouped.computeIfAbsent(type, k -> new ArrayList<>()).add(info.card);
                    }

                    for (Map.Entry<Integer, List<Integer>> typeEntry : typeGrouped.entrySet()) {
                        String typeName = getTypeName(typeEntry.getKey());
                        List<Integer> cards = typeEntry.getValue();
                        Collections.sort(cards);

                        List<String> valueStrs = new ArrayList<>();
                        for (int card : cards) {
                            valueStrs.add(String.valueOf(card % 10));
                        }
                        sb.append("  ").append(typeName).append(": ").append(String.join(" ", valueStrs)).append("\n");
                    }
                }
            } else {
                sb.append("❌ 未听牌\n");
            }

            return sb.toString();
        }

        private String getTypeName(int type) {
            switch (type) {
                case 1: return "万";
                case 2: return "筒";
                case 3: return "条";
                default: return "字牌";
            }
        }
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) {
        TingPaiChecker checker = new TingPaiChecker();

        System.out.println("=== 长沙麻将听牌分析测试（含大胡） ===\n");

////        // 测试1：碰碰胡
//        System.out.println("测试1：碰碰胡");
//        List<Integer> hand1 = Arrays.asList(
//                203, 101, 101,                // 1万刻子
//                102, 102, 102,                // 2万刻子
//                103, 103, 103,                // 3万刻子
//                104, 104, 104,                // 4万刻子
//                203                      // 5万对子（将）
//        );
//        TingResult result1 = checker.checkTingPai(hand1);
//        System.out.println(result1);

//        // 测试2：清一色
//        System.out.println("\n测试2：清一色");
        List<Integer> hand2 = Arrays.asList(
                202,202,202,104,105,106,206,206,206,207,208,209,103                   // 7万对子（将）
        );
        TingResult result2 = checker.checkTingPai(hand2);
        System.out.println(result2.isTingPai);
//
//        // 测试3：清一色碰碰胡
//        System.out.println("\n测试3：清一色碰碰胡");
//        List<Integer> hand3 = Arrays.asList(
//                101, 101, 101,                // 1万刻子
//                102, 102, 102,                // 2万刻子
//                103, 103, 103,                // 3万刻子
//                104, 104, 104,                // 4万刻子
//                105, 105                      // 5万对子（将）
//        );
//        TingResult result3 = checker.checkTingPai(hand3);
//        System.out.println(result3);

        // 测试4：普通胡牌（258将）
//        System.out.println("\n测试4：普通胡牌（258将）");
//        List<Integer> hand4 = Arrays.asList(
//                101, 101, 101,                // 1万刻子
//                102, 103, 104,                // 1-2-3万顺子
//                201,                     // 2筒对子（将）
//                302, 303, 304,                // 2-3-4条顺子
//                305, 306, 307                 // 5-6-7条顺子
//        );
//        TingResult result4 = checker.checkTingPai(hand4);
//        System.out.println(result4);
//
//        // 测试5：七对子
//        System.out.println("\n测试5：七对子");
//        List<Integer> hand5 = Arrays.asList(
//                101, 101,                     // 1万对子
//                102, 102,                     // 2万对子
//                103, 103,                     // 3万对子
//                201, 201,                     // 1筒对子
//                202, 202,                     // 2筒对子
//                301, 301,                     // 1条对子
//                302, 302                      // 2条对子
//        );
//        TingResult result5 = checker.checkTingPai(hand5);
//        System.out.println(result5);
    }
}