package com.nokia.sms;

import com.alibaba.fastjson2.JSON;
import com.nokia.common.http.R;
import com.nokia.sms.vo.RequestParams;
import de.siegmar.fastcsv.reader.CsvReader;
import de.siegmar.fastcsv.reader.CsvRow;
import de.siegmar.fastcsv.writer.CsvWriter;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;

/**
 * 并发测试
 */
@Slf4j
@SpringBootTest
@Import(ConcurrentTest.ConcurrentTestConfig.class)
class ConcurrentTest {
    public static final int poolSize = 40;
    /**
     * 测试结果保存文件夹
     */
    public static final String TEST_DIR = "test/result/";
    /**
     * 手机号文件
     */
    public static final String PHONES_FILE = "test/1000.txt";
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @TestConfiguration
    static class ConcurrentTestConfig {
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }

        @Bean
        public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
            ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
            scheduler.setPoolSize(poolSize);
            scheduler.setWaitForTasksToCompleteOnShutdown(true);
//        scheduler.setThreadNamePrefix("");
//        scheduler.setAwaitTerminationSeconds(60);
//        scheduler.setErrorHandler(e -> log.error(">>> {}", e.getMessage(), e));
            scheduler.initialize();
            return scheduler;
        }
    }

    @Test
    void testQuery() {
        // 每个请求结果保存的路径
        String singlePath = TEST_DIR + "query/single/";
        // 合并结果保存的路径
        String resultPath = TEST_DIR + "query/";
        // 创建文件夹
        try {
            Files.createDirectories(Paths.get(singlePath));
            Files.createDirectories(Paths.get(resultPath));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        resultPath = resultPath + "黑名单查询接口" + poolSize + "个线程1000条数据测试结果.csv";
        // 接口地址
        String url = "http://192.168.50.3:12120/sms/blacklist/api/query/";
//        String url = "http://133.96.94.108:12120/sms/blacklist/api/query/";
        Set<String> phones = readPhones();
        for (String t : phones) {
            RequestParams dto = new RequestParams();
            dto.setPhone(t);
            dto.setFromSystem("test");
            threadPoolTaskScheduler.submit(new Task(restTemplate, t, url, dto, singlePath));
        }
        // 等待所有任务执行结束
        while (threadPoolTaskScheduler.getActiveCount() > 0) {
        }
        mergeResult(phones, resultPath, singlePath);
    }

    @Test
    void testRemove() {
        // 每个请求结果保存的路径
        String singlePath = TEST_DIR + "remove/single/";
        // 合并结果保存的路径
        String resultPath = TEST_DIR + "remove/";
        // 创建文件夹
        try {
            Files.createDirectories(Paths.get(singlePath));
            Files.createDirectories(Paths.get(resultPath));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        resultPath = resultPath + "一键解封接口" + poolSize + "个线程1000条数据测试结果.csv";
        // 接口地址
        String url = "http://133.96.94.108:12120/sms/blacklist/api/remove/";
        Set<String> phones = readPhones();
        for (String t : phones) {
            RequestParams dto = new RequestParams();
            dto.setPhone(t);
            dto.setFromSystem("test");
            dto.setOperator("test");
            threadPoolTaskScheduler.submit(new Task(restTemplate, t, url, dto, singlePath));
        }
        // 等待所有任务执行结束
        while (threadPoolTaskScheduler.getActiveCount() > 0) {
        }
        mergeResult(phones, resultPath, singlePath);
    }

    /**
     * 合并结果
     *
     * @param phones     手机列表
     * @param resultPath 合并结果保存路径
     * @param singlePath 单个结果保存路径
     */
    private void mergeResult(Set<String> phones, String resultPath, String singlePath) {
        try (OutputStreamWriter osw = new OutputStreamWriter(Files.newOutputStream(Paths.get(resultPath)),
                StandardCharsets.UTF_8);
             CsvWriter csvWriter = CsvWriter.builder().build(osw);) {
            csvWriter.writeRow("入参", "时间", "调用时长", "结果");
            for (String tt : phones) {
                Path path = Paths.get(singlePath + tt + ".csv");
                try (CsvReader csvReader = CsvReader.builder().build(path, StandardCharsets.UTF_8)) {
                    for (CsvRow row : csvReader) {
                        csvWriter.writeRow(row.getField(0), row.getField(1), row.getField(2),
                                row.getField(3));
                    }
                } catch (IOException e) {
                    log.error("{} -> {}", tt, e.getMessage(), e);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @AllArgsConstructor
    class Task implements Runnable {
        private RestTemplate restTemplate;
        private String phoneNumber;
        private String url;
        private Object dto;
        private String singlePath;

        @Override
        public void run() {
            Object res;
            String filePath = singlePath + phoneNumber + ".csv";
            LocalDateTime startTime = LocalDateTime.now();
            long startTimestamp = System.currentTimeMillis();
            try {
                res = restTemplate.postForObject(url, dto, R.class);
            } catch (Exception e) {
                res = e.getMessage();
                log.error("{} -> {}", phoneNumber, e.getMessage(), e);
            }
            long endTimestamp = System.currentTimeMillis();
            long cost = endTimestamp - startTimestamp;
            // 记录请求结果
            try (OutputStreamWriter osw = new OutputStreamWriter(Files.newOutputStream(Paths.get(filePath)),
                    StandardCharsets.UTF_8);
                 CsvWriter csv = CsvWriter.builder().build(osw);) {
                csv.writeRow(JSON.toJSONString(dto), startTime.toString(), String.valueOf(cost), JSON.toJSONString(res));
            } catch (IOException e) {
                log.error("{} cost {} -> {}", phoneNumber, cost, e.getMessage(), e);
            }
        }
    }

    /**
     * 读取手机号放入set去重
     */
    Set<String> readPhones() {
        Set<String> result = new HashSet<>();
        try (InputStreamReader isr = new InputStreamReader(Files.newInputStream(Paths.get(PHONES_FILE)),
                StandardCharsets.UTF_8);
             BufferedReader br = new BufferedReader(isr);) {
            String line;
            while ((line = br.readLine()) != null) {
                result.add(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}
