package com.jeesharp.business.modules.setting.application;

import com.jeesharp.business.modules.setting.enums.PrivacyPolicyClientEnum;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 隐私政策富文本「端适配器」。
 * <p>
 * 数据库存储后台编辑器原生 HTML（以 PC 为基准），通过本适配器在 Controller 输出时
 * 针对不同前端环境做最小必要的改写，保证：
 * <ul>
 *     <li>PC 后台：原样返回，便于编辑回填；</li>
 *     <li>H5 浏览器：去除危险标签 / 事件属性，图片表格自适应；</li>
 *     <li>微信小程序 {@code <rich-text>}：在 H5 之上额外剥离 class、iframe、video，
 *     并把不支持的 {@code <table>} 降级为段落，避免页面渲染空白。</li>
 * </ul>
 * 实现仅依赖 JDK 正则，不引入 jsoup 等额外依赖；正则采用懒匹配 + 字符类约束，
 * 输入限定为后台编辑器输出，无需考虑任意 HTML 解析的健壮性。
 *
 * @author jeesharp
 * @since 2026-05-29
 */
public final class PrivacyPolicyContentAdapter {

    private PrivacyPolicyContentAdapter() {
    }

    /** 危险/不安全标签：在任意端都剥离。 */
    private static final Pattern DANGEROUS_TAGS = Pattern.compile(
            "<\\s*(script|style)\\b[\\s\\S]*?<\\s*/\\s*\\1\\s*>",
            Pattern.CASE_INSENSITIVE);

    /** on* 内联事件属性。 */
    private static final Pattern INLINE_EVENT_ATTR = Pattern.compile(
            "\\s+on[a-zA-Z]+\\s*=\\s*\"[^\"]*\"",
            Pattern.CASE_INSENSITIVE);

    /** javascript: 协议链接。 */
    private static final Pattern JAVASCRIPT_PROTOCOL = Pattern.compile(
            "(href|src)\\s*=\\s*\"\\s*javascript:[^\"]*\"",
            Pattern.CASE_INSENSITIVE);

    /** 图片标签起始片段（不含 /> 或 >）。 */
    private static final Pattern IMG_TAG = Pattern.compile(
            "<img\\b([^>]*)>",
            Pattern.CASE_INSENSITIVE);

    /** 表格起始标签。 */
    private static final Pattern TABLE_OPEN = Pattern.compile(
            "<table\\b([^>]*)>",
            Pattern.CASE_INSENSITIVE);

    /** 小程序不支持的多媒体/嵌入式标签，整体剥离。 */
    private static final Pattern MP_DROP_BLOCKS = Pattern.compile(
            "<\\s*(iframe|video|audio|embed|object)\\b[\\s\\S]*?<\\s*/\\s*\\1\\s*>",
            Pattern.CASE_INSENSITIVE);

    /** 小程序不识别 class 属性，统一剥离。 */
    private static final Pattern CLASS_ATTR = Pattern.compile(
            "\\s+class\\s*=\\s*\"[^\"]*\"",
            Pattern.CASE_INSENSITIVE);

    /** 小程序整张表格降级提示。 */
    private static final Pattern TABLE_BLOCK = Pattern.compile(
            "<table\\b[\\s\\S]*?</table>",
            Pattern.CASE_INSENSITIVE);

    /** 小程序锚点降级：保留文字、剥离 href（rich-text 不会响应点击）。 */
    private static final Pattern ANCHOR_TAG = Pattern.compile(
            "<a\\b[^>]*>([\\s\\S]*?)</a>",
            Pattern.CASE_INSENSITIVE);

    /**
     * 适配 HTML。
     *
     * @param html   原始 HTML
     * @param client 目标端
     * @return 适配后的 HTML（null/空 直接原样返回）
     */
    public static String adapt(String html, PrivacyPolicyClientEnum client) {
        if (html == null || html.isEmpty()) {
            return html;
        }
        PrivacyPolicyClientEnum target = client == null ? PrivacyPolicyClientEnum.PC : client;
        return switch (target) {
            case PC -> html;
            case H5 -> adaptForH5(html);
            case MP -> adaptForMp(html);
        };
    }

    private static String stripDangerous(String html) {
        String out = DANGEROUS_TAGS.matcher(html).replaceAll("");
        out = INLINE_EVENT_ATTR.matcher(out).replaceAll("");
        out = JAVASCRIPT_PROTOCOL.matcher(out).replaceAll("$1=\"#\"");
        return out;
    }

    private static String adaptForH5(String html) {
        String out = stripDangerous(html);
        out = injectImgStyle(out);
        out = injectTableStyle(out);
        return out;
    }

    private static String adaptForMp(String html) {
        String out = stripDangerous(html);
        out = MP_DROP_BLOCKS.matcher(out).replaceAll("");
        out = CLASS_ATTR.matcher(out).replaceAll("");
        out = injectImgStyle(out);
        out = downgradeAnchor(out);
        out = downgradeTable(out);
        return out;
    }

    /** 为 {@code <img>} 注入 style，确保自适应；已有 style 时合并而非覆盖。 */
    private static String injectImgStyle(String html) {
        Matcher m = IMG_TAG.matcher(html);
        StringBuilder sb = new StringBuilder(html.length() + 64);
        while (m.find()) {
            String attrs = m.group(1) == null ? "" : m.group(1);
            String merged = mergeStyle(attrs, "max-width:100%;height:auto;");
            m.appendReplacement(sb, Matcher.quoteReplacement("<img" + merged + ">"));
        }
        m.appendTail(sb);
        return sb.toString();
    }

    private static String injectTableStyle(String html) {
        Matcher m = TABLE_OPEN.matcher(html);
        StringBuilder sb = new StringBuilder(html.length() + 64);
        while (m.find()) {
            String attrs = m.group(1) == null ? "" : m.group(1);
            String merged = mergeStyle(attrs, "max-width:100%;border-collapse:collapse;");
            m.appendReplacement(sb, Matcher.quoteReplacement("<table" + merged + ">"));
        }
        m.appendTail(sb);
        return sb.toString();
    }

    private static String downgradeAnchor(String html) {
        Matcher m = ANCHOR_TAG.matcher(html);
        StringBuilder sb = new StringBuilder(html.length());
        while (m.find()) {
            String inner = m.group(1) == null ? "" : m.group(1);
            m.appendReplacement(sb, Matcher.quoteReplacement("<span>" + inner + "</span>"));
        }
        m.appendTail(sb);
        return sb.toString();
    }

    private static String downgradeTable(String html) {
        return TABLE_BLOCK.matcher(html).replaceAll(
                "<p style=\"color:#999;font-size:12px;\">[小程序端不支持表格，请在浏览器查看完整内容]</p>");
    }

    /**
     * 把新增的 style 合并进现有标签属性串：已有 style 则追加在末尾；否则补一段。
     */
    private static String mergeStyle(String attrs, String extraStyle) {
        String trimmed = attrs == null ? "" : attrs;
        Pattern stylePattern = Pattern.compile("style\\s*=\\s*\"([^\"]*)\"", Pattern.CASE_INSENSITIVE);
        Matcher m = stylePattern.matcher(trimmed);
        if (m.find()) {
            String original = m.group(1);
            String separator = original.isBlank() || original.trim().endsWith(";") ? "" : ";";
            String replaced = m.replaceFirst(
                    Matcher.quoteReplacement("style=\"" + original + separator + extraStyle + "\""));
            return replaced;
        }
        if (trimmed.isEmpty() || trimmed.startsWith(" ")) {
            return trimmed + " style=\"" + extraStyle + "\"";
        }
        return " " + trimmed + " style=\"" + extraStyle + "\"";
    }
}
