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

import cn.yunzhixue.ability.center.examsprint.domain.report.AchievementReportContent;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportRenderer;
import cn.yunzhixue.ability.center.examsprint.domain.report.ReportContent;
import cn.yunzhixue.ability.center.examsprint.domain.report.ReportType;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class ClasspathAchievementExamSprintReportRenderer implements ExamSprintReportRenderer {

    private static final String TEMPLATE_RESOURCE = "templates/achievement-exam-sprint-report-template.html";
    private static final Pattern TEMPLATE_PLACEHOLDER_PATTERN = Pattern.compile("\\{\\{([A-Za-z0-9]+)}}");

    private final AchievementExamSprintReportSvgChartBuilder chartBuilder;

    public ClasspathAchievementExamSprintReportRenderer() {
        this(new AchievementExamSprintReportSvgChartBuilder());
    }

    ClasspathAchievementExamSprintReportRenderer(AchievementExamSprintReportSvgChartBuilder chartBuilder) {
        this.chartBuilder = Objects.requireNonNull(chartBuilder, "chartBuilder");
    }

    @Override
    public boolean supports(ReportType reportType) {
        return reportType == ReportType.ACHIEVEMENT;
    }

    @Override
    public String render(ReportContent content, Instant generatedAt) {
        if (!(content instanceof AchievementReportContent reportContent)) {
            throw new IllegalArgumentException("Achievement renderer requires AchievementReportContent");
        }
        try {
            AchievementReportContent.SummaryMetrics summary = reportContent.summaryMetrics();
            AchievementReportContent.Comparison vocabulary = reportContent.vocabularyComparison();
            AchievementReportContent.Comparison paperKnownWords = reportContent.paperKnownWordsComparison();
            AchievementReportContent.StageVocabularySummary stageVocabulary = reportContent.stageVocabularySummary();
            AchievementReportContent.TestPaperVocabularySummary testPaperVocabulary = reportContent.testPaperVocabularySummary();
            AchievementReportContent.ExamUnknownWordsHitStatus hitStatus = reportContent.examUnknownWordsHitStatus();

            return renderTemplate(loadTemplate(), placeholders(reportContent, summary, vocabulary, paperKnownWords, stageVocabulary, testPaperVocabulary, hitStatus));
        } catch (IOException exception) {
            throw new UncheckedIOException("Failed to load achievement exam sprint report template", exception);
        } catch (Exception exception) {
            throw new IllegalStateException("Failed to render achievement exam sprint report", exception);
        }
    }

    private Map<String, String> placeholders(AchievementReportContent reportContent,
                                              AchievementReportContent.SummaryMetrics summary,
                                              AchievementReportContent.Comparison vocabulary,
                                              AchievementReportContent.Comparison paperKnownWords,
                                              AchievementReportContent.StageVocabularySummary stageVocabulary,
                                              AchievementReportContent.TestPaperVocabularySummary testPaperVocabulary,
                                              AchievementReportContent.ExamUnknownWordsHitStatus hitStatus) {
        Map<String, String> placeholders = new LinkedHashMap<>();
        placeholders.put("reportTitle", escape(reportContent.reportTitle()));
        placeholders.put("reportSubtitle", escape(reportContent.reportSubtitle()));
        placeholders.put("completionTitle", escape(reportContent.completionTitle()));
        placeholders.put("completionSubtitle", escape(reportContent.completionSubtitle()));
        placeholders.put("vocabularyGrowthText", escape(summary.vocabularyGrowthText()));
        placeholders.put("paperKnownWordsGrowthText", escape(summary.paperKnownWordsGrowthText()));
        placeholders.put("unknownWordHitRateText", escape(formatPercentRatio(summary.unknownWordHitRateText())));
        placeholders.put("learningEfficiencyText", escape(withUnit(summary.learningEfficiencyText(), "倍")));
        placeholders.put("vocabularyComparisonChart", renderVocabularyComparisonChart(vocabulary));
        placeholders.put("paperKnownWordsComparisonChart", renderPaperKnownWordsComparisonChart(paperKnownWords));
        placeholders.put("vocabularyBeforeText", escape(withSeparatedUnit(vocabulary.beforeText(), "词")));
        placeholders.put("vocabularyAfterText", escape(withSeparatedUnit(vocabulary.afterText(), "词")));
        placeholders.put("vocabularyGrowthDetailText", escape(withSeparatedUnit(vocabulary.growthText(), "词")));
        placeholders.put("stageVocabularyLabel", escape(stageVocabularyLabel(stageVocabulary.stageName())));
        placeholders.put("stageVocabularyText", escape(withSeparatedUnit(stageVocabulary.stageVocabularyText(), "词")));
        placeholders.put("studentVocabMasteryBeforeText", escape(withUnit(stageVocabulary.masteryBeforeText(), "%")));
        placeholders.put("studentVocabMasteryAfterText", escape(withUnit(stageVocabulary.masteryAfterText(), "%")));
        placeholders.put("studentVocabMasteryImprovementText", escape(withUnit(stageVocabulary.masteryImprovementText(), "%")));
        placeholders.put("paperKnownWordsBeforeText", escape(withSeparatedUnit(paperKnownWords.beforeText(), "个")));
        placeholders.put("paperKnownWordsAfterText", escape(withSeparatedUnit(paperKnownWords.afterText(), "个")));
        placeholders.put("paperKnownWordsGrowthDetailText", escape(withSeparatedUnit(paperKnownWords.growthText(), "个")));
        placeholders.put("testPaperTitle", escape(testPaperVocabulary.testPaperTitle()));
        placeholders.put("testPaperWordCountText", escape(withSeparatedUnit(testPaperVocabulary.testPaperWordCountText(), "词")));
        placeholders.put("testPaperUnknownWordsBeforeText", escape(withSeparatedUnit(testPaperVocabulary.unknownWordsBeforeText(), "个")));
        placeholders.put("testPaperUnknownWordsAfterText", escape(withSeparatedUnit(testPaperVocabulary.unknownWordsAfterText(), "个")));
        placeholders.put("testPaperMasteryRateBeforeText", escape(withUnit(testPaperVocabulary.masteryRateBeforeText(), "%")));
        placeholders.put("testPaperMasteryRateAfterText", escape(withUnit(testPaperVocabulary.masteryRateAfterText(), "%")));
        placeholders.put("testPaperMasteryRateImprovementText", escape(withUnit(testPaperVocabulary.masteryRateImprovementText(), "%")));
        placeholders.put("unknownWordsBeforeText", escape(withSeparatedUnit(hitStatus.unknownWordsBeforeText(), "个")));
        placeholders.put("unknownWordsAfterText", escape(withSeparatedUnit(hitStatus.unknownWordsAfterText(), "个")));
        placeholders.put("reducedUnknownWordsText", escape(withSeparatedUnit(hitStatus.reducedUnknownWordsText(), "个")));
        placeholders.put("hitStatusUnknownWordHitRateText", escape(formatPercentRatio(hitStatus.unknownWordHitRateText())));
        placeholders.put("hitStatusLearningEfficiencyText", escape(withUnit(hitStatus.learningEfficiencyText(), "倍")));
        placeholders.put("hitWords", renderHitWords(hitStatus));
        return placeholders;
    }

    private String renderTemplate(String template, Map<String, String> placeholders) {
        Matcher matcher = TEMPLATE_PLACEHOLDER_PATTERN.matcher(template);
        StringBuffer rendered = new StringBuffer();
        while (matcher.find()) {
            String placeholderName = matcher.group(1);
            String replacement = placeholders.getOrDefault(placeholderName, matcher.group(0));
            matcher.appendReplacement(rendered, Matcher.quoteReplacement(replacement));
        }
        matcher.appendTail(rendered);
        return rendered.toString();
    }

    private String stageVocabularyLabel(String stageName) {
        String normalized = normalizeDisplayValue(stageName);
        return normalized.isEmpty() || isTemplatePlaceholder(normalized) ? normalized : normalized + "词汇量";
    }

    private String loadTemplate() throws IOException {
        try (InputStream inputStream = new ClassPathResource(TEMPLATE_RESOURCE).getInputStream()) {
            return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
        }
    }

    private String renderVocabularyComparisonChart(AchievementReportContent.Comparison comparison) {
        return chartBuilder.comparisonBarChart(
                "vocabulary-growth-chart",
                "词汇量对比",
                "训练前",
                safeNonNegativeFinite(comparison.beforeValue()),
                withSeparatedUnit(comparison.beforeText(), "词"),
                "训练后",
                safeNonNegativeFinite(comparison.afterValue()),
                withSeparatedUnit(comparison.afterText(), "词"),
                "#448aff"
        );
    }

    private String renderPaperKnownWordsComparisonChart(AchievementReportContent.Comparison comparison) {
        return chartBuilder.comparisonBarChart(
                "paper-known-words-chart",
                "试卷熟词量对比",
                "训练前",
                safeNonNegativeFinite(comparison.beforeValue()),
                withSeparatedUnit(comparison.beforeText(), "个"),
                "训练后",
                safeNonNegativeFinite(comparison.afterValue()),
                withSeparatedUnit(comparison.afterText(), "个"),
                "#34a853"
        );
    }

    private String renderHitWords(AchievementReportContent.ExamUnknownWordsHitStatus hitStatus) {
        if (hitStatus == null || hitStatus.hitWords() == null || hitStatus.hitWords().isEmpty()) {
            return "<div class=\"word-empty\">暂无命中单词</div>";
        }

        StringBuilder builder = new StringBuilder();
        for (String hitWord : hitStatus.hitWords()) {
            builder.append("<div class=\"word-item\">")
                    .append(escape(hitWord))
                    .append("</div>");
        }
        return builder.toString();
    }

    private double safeNonNegativeFinite(Double value) {
        if (value == null || !Double.isFinite(value) || value < 0d) {
            return 0d;
        }
        return value;
    }

    private String withSeparatedUnit(String value, String unit) {
        String normalized = normalizeDisplayValue(value);
        if (normalized.isEmpty() || isTemplatePlaceholder(normalized) || normalized.endsWith(unit) || normalized.endsWith(" " + unit)) {
            return normalized;
        }
        return normalized + " " + unit;
    }

    private String withUnit(String value, String unit) {
        String normalized = normalizeDisplayValue(value);
        if (normalized.isEmpty() || isTemplatePlaceholder(normalized) || normalized.endsWith(unit)) {
            return normalized;
        }
        return normalized + unit;
    }

    private String formatPercentRatio(String value) {
        String normalized = normalizeDisplayValue(value);
        if (normalized.isEmpty() || isTemplatePlaceholder(normalized) || normalized.endsWith("%")) {
            return normalized;
        }
        try {
            double ratio = Double.parseDouble(normalized);
            if (!Double.isFinite(ratio)) {
                return normalized;
            }
            return trimTrailingZeros(ratio * 100d) + "%";
        } catch (NumberFormatException ignored) {
            return normalized;
        }
    }

    private String trimTrailingZeros(double value) {
        if (!Double.isFinite(value)) {
            return "0";
        }
        String formatted = String.format(Locale.ROOT, "%.2f", value);
        return formatted.replaceAll("\\.0+$", "").replaceAll("(\\.\\d*?)0+$", "$1");
    }

    private String normalizeDisplayValue(String value) {
        return value == null ? "" : value.trim();
    }

    private boolean isTemplatePlaceholder(String value) {
        return value.startsWith("{{") && value.endsWith("}}");
    }

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