package cn.yunzhixue.ability.center.examsprint.infrastructure.report.rendering.achievement;

import java.util.Locale;
import java.util.regex.Pattern;

public class AchievementExamSprintReportSvgChartBuilder {

    private static final Pattern CSS_CLASS_PATTERN = Pattern.compile("^[A-Za-z0-9_ -]+$");
    private static final Pattern HEX_COLOR_PATTERN = Pattern.compile("^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$");
    private static final String DEFAULT_FILL_COLOR = "#ff7d00";
    private static final String SVG_CJK_FONT_FAMILY = " font-family=\"'MiSans VF', MiSans, ReportFont, sans-serif\"";

    public String comparisonBarChart(String cssClass,
                                     String ariaLabel,
                                     String beforeLabel,
                                     double beforeValue,
                                     String beforeText,
                                     String afterLabel,
                                     double afterValue,
                                     String afterText,
                                     String fillColor) {
        double maxValue = Math.max(Math.max(beforeValue, afterValue), 1d);
        int axisLeft = 58;
        int axisBottom = 180;
        int axisTop = 36;
        int axisRight = 324;
        int beforeX = 118;
        int afterX = 222;
        int barWidth = 58;
        int plotHeight = axisBottom - axisTop;
        int beforeHeight = barHeight(beforeValue, maxValue, plotHeight);
        int afterHeight = barHeight(afterValue, maxValue, plotHeight);
        String barFillColor = safeColor(fillColor);

        return new StringBuilder()
                .append("<svg class='achievement-bar-chart ").append(safeCssClass(cssClass))
                .append("'").append(SVG_CJK_FONT_FAMILY)
                .append(" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 220' role='img' aria-label='")
                .append(escape(ariaLabel)).append("'>")
                .append(renderAxisGridAndTicks(maxValue, axisLeft, axisRight, axisTop, axisBottom))
                .append("<rect class='chart-bar chart-bar-before' x='").append(beforeX)
                .append("' y='").append(axisBottom - beforeHeight)
                .append("' width='").append(barWidth).append("' height='").append(beforeHeight)
                .append("' rx='10' ry='10' fill='").append(barFillColor).append("'/>")
                .append("<rect class='chart-bar chart-bar-after' x='").append(afterX)
                .append("' y='").append(axisBottom - afterHeight)
                .append("' width='").append(barWidth).append("' height='").append(afterHeight)
                .append("' rx='10' ry='10' fill='").append(barFillColor).append("'/>")
                .append("<text class='chart-value' x='").append(beforeX + barWidth / 2)
                .append("' y='").append(Math.max(18, axisBottom - 10 - beforeHeight))
                .append("' text-anchor='middle'>").append(escape(beforeText)).append("</text>")
                .append("<text class='chart-value' x='").append(afterX + barWidth / 2)
                .append("' y='").append(Math.max(18, axisBottom - 10 - afterHeight))
                .append("' text-anchor='middle'>").append(escape(afterText)).append("</text>")
                .append("<text class='chart-label' x='").append(beforeX + barWidth / 2)
                .append("' y='").append(axisBottom + 22).append("' text-anchor='middle'>")
                .append(escape(beforeLabel)).append("</text>")
                .append("<text class='chart-label' x='").append(afterX + barWidth / 2)
                .append("' y='").append(axisBottom + 22).append("' text-anchor='middle'>")
                .append(escape(afterLabel)).append("</text>")
                .append("</svg>")
                .toString();
    }

    private String renderAxisGridAndTicks(double maxValue, int axisLeft, int axisRight, int axisTop, int axisBottom) {
        double[] tickValues = {maxValue, maxValue / 2d, 0d};
        StringBuilder builder = new StringBuilder();
        for (double tickValue : tickValues) {
            int y = tickY(tickValue, maxValue, axisTop, axisBottom);
            builder.append("<line class='chart-grid-line' x1='").append(axisLeft)
                    .append("' y1='").append(y)
                    .append("' x2='").append(axisRight)
                    .append("' y2='").append(y)
                    .append("' stroke='#edf2f7' stroke-width='1'/>")
                    .append("<line class='chart-tick' x1='").append(axisLeft - 4)
                    .append("' y1='").append(y)
                    .append("' x2='").append(axisLeft)
                    .append("' y2='").append(y)
                    .append("' stroke='#a9b4c2' stroke-width='1'/>")
                    .append("<text class='chart-tick-label' x='").append(axisLeft - 8)
                    .append("' y='").append(y + 4)
                    .append("' text-anchor='end'>")
                    .append(formatTickLabel(tickValue))
                    .append("</text>");
        }
        builder.append("<line class='chart-y-axis' x1='").append(axisLeft)
                .append("' y1='").append(axisTop)
                .append("' x2='").append(axisLeft)
                .append("' y2='").append(axisBottom)
                .append("' stroke='#9aa6b2' stroke-width='1'/>")
                .append("<line class='chart-x-axis' x1='").append(axisLeft)
                .append("' y1='").append(axisBottom)
                .append("' x2='").append(axisRight)
                .append("' y2='").append(axisBottom)
                .append("' stroke='#9aa6b2' stroke-width='1'/>");
        return builder.toString();
    }

    private int tickY(double value, double maxValue, int axisTop, int axisBottom) {
        if (maxValue <= 0d) {
            return axisBottom;
        }
        double ratio = Math.max(0d, Math.min(1d, value / maxValue));
        return axisBottom - (int) Math.round(ratio * (axisBottom - axisTop));
    }

    private String formatTickLabel(double value) {
        return String.format(Locale.ROOT, "%.0f", Math.max(0d, value));
    }

    private int barHeight(double value, double maxValue, int plotHeight) {
        if (value <= 0d || maxValue <= 0d) {
            return 0;
        }
        double ratio = Math.max(0d, Math.min(1d, value / maxValue));
        return (int) Math.round(ratio * plotHeight);
    }

    private String safeCssClass(String value) {
        if (value == null) {
            return "";
        }
        String trimmed = value.trim();
        if (CSS_CLASS_PATTERN.matcher(trimmed).matches()) {
            return trimmed;
        }
        return "";
    }

    private String safeColor(String value) {
        if (value == null) {
            return DEFAULT_FILL_COLOR;
        }
        String trimmed = value.trim().toLowerCase(Locale.ROOT);
        if (HEX_COLOR_PATTERN.matcher(trimmed).matches()) {
            return trimmed;
        }
        return DEFAULT_FILL_COLOR;
    }

    private String escape(String value) {
        if (value == null) {
            return "";
        }
        return value.replace("&", "&amp;")
                .replace("<", "&lt;")
                .replace(">", "&gt;")
                .replace("\"", "&quot;")
                .replace("'", "&#39;");
    }
}
