package net.christophe.genin.spring.boot.pdfgenerator.core;


import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class PdfBuilderTest {

    public static final String TARGET_TMP_TEST = "target/tmp-test";

    @Autowired
    PdfGeneratorBuilder builder;
    private Map<String, Object> parameters;

    private SectionView section1;

    public class Pair {
        public Pair(String key, String value) {
            this.key = key;
            this.value = value;
        }
        public String key;
        public String value;
    }

    public class SectionView {
        public boolean isEmpty = false;

        public String seq = "0.0";

        public String title = "No title";

        /*
         * 1. 先处理xhtml， replace {{1.1_1}} with {{1.1_score}}
         * 2. 添加 {{1.1_score}} 等到 parameters
         * 3. xhtml  key: value.
         * 
         * 表格变量顺序从左到右，从上到下。合并表格取左上。
         */
        public List<Pair> data = new ArrayList<>();

        public List<SectionView> views = new ArrayList<>();

        public Map<String, String> getKeyMapping() {
            HashMap<String, String> ret = new HashMap<>();
            for(int i = 0; i < data.size(); i++) {
                ret.put(
                    String.format("%s_%d", this.seq, i), 
                    String.format("%s_%s", this.seq, data.get(i).key));
            }
            for(SectionView sv: this.views) {
                ret.putAll(sv.getKeyMapping());
            }
            return ret;
        }

        public Map<String, String> getKeyValues() {
            HashMap<String, String> ret = new HashMap<>();
            ret.put(String.format("%s__seq", this.seq), this.seq);
            ret.put(String.format("%s__title", this.seq), this.title);
            for(int i = 0; i < data.size(); i++) {
                ret.put(
                    String.format("%s_%s", this.seq, data.get(i).key),
                    data.get(i).value
                );
            }
            for(SectionView sv: this.views) {
                ret.putAll(sv.getKeyValues());
            }
            return ret;
        }
    }

    @Before
    public void initialize() {
        new File(TARGET_TMP_TEST).mkdirs();

        parameters = new HashMap<>();
        parameters.put("next", "42");

        section1 = new SectionView();
        section1.seq = "1";
        section1.title = "报告概述";

        section1.views.add(new SectionView());
        SectionView section1_1 = section1.views.get(0);
        section1_1.seq = "1.1";
        section1_1.title = "审核结果";
        section1_1.data.add(new Pair("score", "77"));
        section1_1.data.add(new Pair("result", "已通过"));
        section1_1.data.add(new Pair("name", "上海成泰医药科技有限公司"));
        section1_1.data.add(new Pair("date", "2025年2月 26日"));
        section1_1.data.add(new Pair("version", "PLUS版"));

        section1.views.add(new SectionView());
        SectionView section1_2 = section1.views.get(1);
        section1_2.seq = "1.2";
        section1_2.title = "风险概述";
        section1_2.data.add(new Pair("tips", "-"));
        section1_2.data.add(new Pair("suggestion", "审核试用"));

        section1.views.add(new SectionView());
        SectionView section1_3 = section1.views.get(2);
        section1_3.seq = "1.3";
        section1_3.title = "评分情况";
        section1_3.data.add(new Pair("typeScore", "3"));
        section1_3.data.add(new Pair("ageScore", "3"));
        section1_3.data.add(new Pair("claimCapitalScore", "3"));
        section1_3.data.add(new Pair("realCapitalScore", "3"));
        section1_3.data.add(new Pair("publicRecordScore", "20"));
        section1_3.data.add(new Pair("financeScore", "40"));
        section1_3.data.add(new Pair("antiBribeScore", "5"));
    }

    private String preprocess(SectionView sv, String path) throws IOException {
        File file = new File(path.replace(".xhtml", ".processed.xhtml"));
        if (file.exists()) {
            file.delete();
        }
        BufferedReader reader = new BufferedReader(new FileReader(path));
        String fileContent = reader.lines().collect(Collectors.joining(System.lineSeparator()));
        reader.close();
        for(Map.Entry<String, String> entry : sv.getKeyMapping().entrySet()) {
            fileContent = fileContent.replace(entry.getKey(), entry.getValue());
        }
        BufferedWriter writer = new BufferedWriter(new FileWriter(file.toString()));
        writer.write(fileContent);
        writer.close();
        return file.toString();
    }

    @Test
    public void should_generate_pdf_from_file_system() {
        Generator generator = builder.create().withFileSystem("src/test/resources/test.html").build();
        assertNotNull(generator);
        byte[] bytes = generator.toBytes(parameters);
        assertNotNull(bytes);
        assertTrue(bytes.length > 0);
    }

    @Test
    public void should_generate_pdf_from_classpath() {
        Generator generator = builder.create()
                .withClasspathResource("test.html")
                .build();
        assertNotNull(generator);
        byte[] bytes = generator.toBytes(parameters);
        assertNotNull(bytes);
        assertTrue(bytes.length > 0);
    }

    @Test(expected = Exception.class)
    public void should_throw_exception_if_no_type() {
        builder.create().build();
    }

    @Test
    public void should_generate_pdf_from_string() {
        Generator generator = builder.create()
                .withCompilerOptions(template -> {
                    return template.escapeHTML(true);
                })
                .withXhtml("<html><body>The response is {{next}}</body></html>")
                .build();
        assertNotNull(generator);
        byte[] bytes = generator.toBytes(parameters);
        assertNotNull(bytes);
        assertTrue(bytes.length > 0);
    }

    @Test
    public void test_cso_report() throws IOException {
        String tmpl = "src/test/resources/cso_report.xhtml";
        String processedTmpl = preprocess(section1, tmpl);

        Generator generator = builder.create().withFileSystem(processedTmpl).build();
        File file = new File(TARGET_TMP_TEST + "/cso_report.pdf");
        if (file.exists()) {
            file.delete();
        }

        parameters.putAll(section1.getKeyValues());
        generator.toFile(parameters, file);
        assertTrue(file.exists());
    }
}
