package com.aicheck.notify;

import com.aicheck.model.ApiEndpoint;
import com.aicheck.model.ClassChangeReport;
import com.aicheck.model.ClassType;
import com.aicheck.model.FieldChange;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 将 ClassChangeReport 渲染为企业微信 Markdown 并发送（或仅日志输出）。
 * <p>
 * 使用 webhook {@code markdown}（v1）：引用块 + 换行排版，三色 font（info/comment/warning）。
 * v1 不支持无序列表，各项以 {@code >标签:  值} 分行展示（冒号后两空格）。
 */
public class WeComNotifier {
    private static final int MAX_LENGTH = 3800;
    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

    private final OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .build();

    /** 逐条发送企微通知，返回成功条数 */
    public int sendAll(String webhookUrl, List<ClassChangeReport> reports, String modifier, String modifyTime) {
        if (reports == null || reports.isEmpty()) {
            System.out.println("无类变更，不发送到企业微信");
            return 0;
        }

        int sent = 0;
        for (ClassChangeReport report : reports) {
            String markdown = buildMarkdown(report, modifier, modifyTime);
            if (postMarkdown(webhookUrl, markdown)) {
                sent++;
                System.out.println("已发送类变更通知: " + report.getClassName());
            }
        }
        if (sent > 0) {
            System.out.println("总共发送 " + sent + " 条类变更通知到企业微信");
        }
        return sent;
    }

    /** 企微关闭时打印 Markdown 到控制台 */
    public void logAll(List<ClassChangeReport> reports, String modifier, String modifyTime) {
        if (reports == null || reports.isEmpty()) {
            System.out.println("无类变更，无日志输出");
            return;
        }

        System.out.println("企业微信通知已关闭（wecom.enabled=false），以下结果仅输出到日志：");
        for (int i = 0; i < reports.size(); i++) {
            ClassChangeReport report = reports.get(i);
            System.out.println("========== 类变更 [" + (i + 1) + "/" + reports.size()
                    + "]: " + report.getClassName() + " ==========");
            System.out.println(buildMarkdown(report, modifier, modifyTime));
            System.out.println("========== 结束 ==========");
        }
        System.out.println("共 " + reports.size() + " 条类变更结果（未发送到企业微信）");
    }

    /** 组装完整 Markdown 正文（引用块 + 换行，每项独立一行） */
    public String buildMarkdown(ClassChangeReport report, String modifier, String modifyTime) {
        StringBuilder sb = new StringBuilder();
        sb.append("# 【类变更通知】").append("\n\n");
        appendHeader(sb, report, modifier, modifyTime);

        sb.append("\n## 【对象变更细节】").append("\n\n");
        appendChangeDetails(sb, report);

        sb.append("\n## 【影响范围】").append("\n\n");
        appendImpactSections(sb, report);
        return truncate(sb.toString());
    }

    /** 变更对象行：类名（绿）+ 可选中文说明（灰，整行加粗） */
    private String formatChangeTarget(ClassChangeReport report) {
        String name = colorInfo(safe(report.getClassName()));
        String description = report.getClassDescription();
        if (description == null || description.isBlank()) {
            return name;
        }
        return name + "（" + colorComment(description) + "）";
    }

    /** 头部元信息，每项一行引用（加粗） */
    private void appendHeader(StringBuilder sb, ClassChangeReport report,
                              String modifier, String modifyTime) {
        sb.append(quoteKvBold("变更对象", formatChangeTarget(report))).append("\n");
        sb.append(quoteKvBold("修改人", colorComment(modifier))).append("\n");
        sb.append(quoteKvBold("时间", colorComment(modifyTime))).append("\n");
        sb.append(quoteKvBold("路径", colorComment(report.getSourceFile()))).append("\n");
    }

    /** 渲染删除 / 重命名 / 字段变更 */
    private void appendChangeDetails(StringBuilder sb, ClassChangeReport report) {
        if (report.isDeleted()) {
            sb.append(quoteLine(colorWarning("[已删除]") + "  "
                    + colorComment("该类文件已被移除"))).append("\n");
            return;
        }

        if (report.isRenamed()) {
            sb.append(quoteLine(colorWarning("[类名变更]") + "  "
                    + colorComment(safe(report.getOldClassName())) + " → "
                    + colorInfo(safe(report.getClassName())))).append("\n");
        }

        if (report.isRenameOnly()) {
            sb.append(quoteLine(colorComment("字段无变化"))).append("\n");
            return;
        }

        if (!report.getFieldChanges().isEmpty()) {
            int count = report.getFieldChanges().size();
            sb.append(quoteLine("**共 " + colorWarning(String.valueOf(count)) + " 项变更**"))
                    .append("\n\n");
            for (int i = 0; i < report.getFieldChanges().size(); i++) {
                if (i > 0) {
                    sb.append("\n");
                }
                sb.append(formatFieldChange(report.getFieldChanges().get(i)));
            }
            sb.append("\n");
        }
    }

    /** 按类类型选择影响段落 */
    private void appendImpactSections(StringBuilder sb, ClassChangeReport report) {
        appendImpactByType(sb, report);
    }

    /** Dto/Vo/Entity/Model 各展示不同的 request/response/转换段落 */
    private void appendImpactByType(StringBuilder sb, ClassChangeReport report) {
        switch (report.getClassType()) {
            case DTO:
                appendSectionIfNeeded(sb, report, true, false, true);
                break;
            case VO:
                appendSectionIfNeeded(sb, report, false, true, true);
                break;
            case ENTITY:
            case MODEL:
                appendSectionIfNeeded(sb, report, false, false, true);
                break;
            default:
                appendSectionIfNeeded(sb, report, true, true, true);
        }
    }

    /** 按需追加 request / response / 类转换三个小节 */
    private void appendSectionIfNeeded(StringBuilder sb, ClassChangeReport report,
                                       boolean showRequest, boolean showResponse, boolean showConversion) {
        if (showRequest) {
            sb.append("### 影响 request 接口").append("\n");
            appendEndpointList(sb, report.getInputImpactEndpoints());
            sb.append("\n");
        }
        if (showResponse) {
            sb.append("### 影响 response 接口").append("\n");
            appendEndpointList(sb, report.getFrontendImpactEndpoints());
            sb.append("\n");
        }
        if (showConversion) {
            sb.append("### 类转换影响").append("\n");
            appendConversionList(sb, report);
        }
    }

    /** 渲染关联 Entity，每项一行 */
    private void appendConversionList(StringBuilder sb, ClassChangeReport report) {
        if (!report.isConversionCheckEnabled()) {
            sb.append(quoteLine(colorComment("未开启检测"))).append("\n");
            return;
        }
        if (report.getConversionEntities().isEmpty()) {
            sb.append(quoteLine(colorComment("无"))).append("\n");
            return;
        }
        for (String entity : report.getConversionEntities()) {
            sb.append(quoteKv("Entity", colorInfo(safe(entity)))).append("\n");
        }
    }

    /** 渲染接口，每项一行 */
    private void appendEndpointList(StringBuilder sb, List<ApiEndpoint> endpoints) {
        if (endpoints == null || endpoints.isEmpty()) {
            sb.append(quoteLine(colorComment("无"))).append("\n");
            return;
        }
        for (ApiEndpoint endpoint : endpoints) {
            sb.append(formatEndpointLine(endpoint)).append("\n");
        }
    }

    /** 接口行：> POST `/path` */
    private String formatEndpointLine(ApiEndpoint endpoint) {
        String line = endpoint.displayLine();
        int space = line.indexOf(' ');
        if (space > 0) {
            String method = line.substring(0, space).trim();
            String path = line.substring(space).trim();
            return quoteLine(colorInfo(method) + " " + inlineCode(path));
        }
        return quoteLine(inlineCode(safe(line)));
    }

    /** 单条字段变更：标签、说明、类型合并为一行，字段间空行分隔 */
    private String formatFieldChange(FieldChange change) {
        String fieldName = inlineCode(safe(change.getFieldName()));
        String desc = change.getDescription() == null ? "" : change.getDescription();
        String descPart = desc.isBlank()
                ? colorComment("（无说明）")
                : colorComment(desc);

        switch (change.getKind()) {
            case ADDED: {
                StringBuilder line = new StringBuilder();
                line.append(tagAdded()).append("  ").append(fieldName)
                        .append("  说明:  ").append(descPart);
                appendFieldType(line, change);
                return quoteLine(line.toString());
            }
            case REMOVED: {
                StringBuilder line = new StringBuilder();
                line.append(tagRemoved()).append("  ").append(fieldName)
                        .append("  说明:  ").append(descPart);
                appendFieldType(line, change);
                return quoteLine(line.toString());
            }
            case RENAMED: {
                StringBuilder renameLine = new StringBuilder();
                renameLine.append(tagRenamed()).append("  ")
                        .append(colorComment(safe(change.getOldFieldName()))).append(" → ")
                        .append(colorInfo(safe(change.getFieldName())))
                        .append("  说明:  ").append(descPart);
                appendFieldType(renameLine, change);
                return quoteLine(renameLine.toString());
            }
            case MODIFIED:
            default: {
                StringBuilder line = new StringBuilder();
                line.append(tagModified()).append("  ").append(fieldName)
                        .append("  说明:  ").append(descPart);
                appendFieldType(line, change);
                return quoteLine(line.toString());
            }
        }
    }

    /** 追加字段类型：新增/重命名(仅改名)用 info，删除用 warning，修改/重命名(改类型)用 old → new */
    private void appendFieldType(StringBuilder line, FieldChange change) {
        if (change.getKind() == FieldChange.ChangeKind.RENAMED
                || change.getKind() == FieldChange.ChangeKind.MODIFIED) {
            String typeDetail = change.getDetail();
            if (typeDetail != null && !typeDetail.isBlank()) {
                line.append("  类型:  ").append(formatTypeChange(typeDetail));
                return;
            }
        }
        String singleType = change.getKind() == FieldChange.ChangeKind.REMOVED
                ? change.getOldType()
                : change.getNewType();
        if (singleType == null || singleType.isBlank()) {
            return;
        }
        line.append("  类型:  ");
        if (change.getKind() == FieldChange.ChangeKind.REMOVED) {
            line.append(colorWarning(singleType));
        } else {
            line.append(colorInfo(singleType));
        }
    }

    /** 类型变化：旧 warning → 新 info；泛型尖括号原样展示，不做 HTML 转义 */
    private String formatTypeChange(String detail) {
        int arrow = detail.indexOf(" → ");
        if (arrow < 0) {
            return colorWarning(detail);
        }
        String oldType = detail.substring(0, arrow).trim();
        String newType = detail.substring(arrow + 3).trim();
        return colorWarning(oldType) + " → " + colorInfo(newType);
    }

    private String tagAdded() {
        return colorInfo("[新增]");
    }

    private String tagRemoved() {
        return colorWarning("[删除]");
    }

    private String tagModified() {
        return colorWarning("[修改]");
    }

    private String tagRenamed() {
        return colorWarning("[重命名]");
    }

    /** 引用行：{@code >标签:  值}（冒号后两空格） */
    private String quoteKv(String key, String value) {
        return "> " + key + ":  " + value;
    }

    /** 加粗引用行：用于类变更通知头部 */
    private String quoteKvBold(String key, String value) {
        return "> **" + key + ":  " + value + "**";
    }

    /** 纯引用行 */
    private String quoteLine(String content) {
        return "> " + content;
    }

    /** 行内代码 */
    private String inlineCode(String text) {
        return "`" + text.replace("`", "'") + "`";
    }

    private String colorInfo(String text) {
        return "<font color=\"info\">" + text + "</font>";
    }

    private String colorComment(String text) {
        return "<font color=\"comment\">" + safe(text) + "</font>";
    }

    private String colorWarning(String text) {
        return "<font color=\"warning\">" + text + "</font>";
    }

    /** 转义 HTML 特殊字符，避免破坏 font 标签 */
    private String safe(String text) {
        if (text == null) {
            return "";
        }
        return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
    }

    /** POST 企微 Webhook（markdown v1） */
    private boolean postMarkdown(String webhookUrl, String content) {
        if (webhookUrl == null || webhookUrl.isBlank() || webhookUrl.contains("YOUR_WECOM")) {
            System.out.println("[警告] 未配置有效的企业微信 Webhook URL");
            System.out.println("--- 通知预览 ---");
            System.out.println(content.length() > 1000 ? content.substring(0, 1000) : content);
            return false;
        }

        String payload = "{\"msgtype\":\"markdown\",\"markdown\":{\"content\":"
                + jsonEscape(content) + "}}";
        Request request = new Request.Builder()
                .url(webhookUrl)
                .post(RequestBody.create(payload, JSON))
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                String body = response.body().string();
                return body.contains("\"errcode\":0");
            }
            System.out.println("[错误] 企微返回异常: " + response.code()
                    + (response.body() != null ? " " + response.body().string() : ""));
            return false;
        } catch (IOException e) {
            System.out.println("[错误] 发送企微消息失败: " + e.getMessage());
            return false;
        }
    }

    /** 超长消息截断（企微上限 4096 字节 UTF-8） */
    private String truncate(String text) {
        if (text.length() <= MAX_LENGTH) {
            return text;
        }
        return text.substring(0, MAX_LENGTH) + "\n\n... 消息过长，已截断";
    }

    /** JSON 字符串转义 */
    private String jsonEscape(String text) {
        String escaped = text
                .replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("\n", "\\n")
                .replace("\r", "");
        return "\"" + escaped + "\"";
    }
}
