package com.nokia.domainb.capability.controller;

import com.alibaba.fastjson2.JSON;
import com.nokia.common.http.R;
import com.nokia.domainb.dto.*;
import com.nokia.domainb.util.Utils;
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 okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
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.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
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.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@SpringBootTest
@Import(DomainControllerTest.ConcurrentTestConfig.class)
class DomainControllerTest {
    public static final int poolSize = 10;
    /**
     * 测试结果保存文件夹
     */
    public static final String TEST_DIR = "z:/";
    @Autowired
    private RestTemplate template;
    @Autowired
    private ThreadPoolExecutor executor;

    @TestConfiguration
    static class ConcurrentTestConfig {
        @Bean
        public RestTemplate template() {
            return new RestTemplate(new OkHttp3ClientHttpRequestFactory(new OkHttpClient().newBuilder()
                    .connectionPool(new ConnectionPool(1000, 10, TimeUnit.MINUTES))
                    .build()));
        }

        @Bean
        public ThreadPoolExecutor executor() {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(), new CustomizableThreadFactory("test-"));
            executor.prestartAllCoreThreads();
            return executor;
        }
    }

    /**
     * 欠费接口整合并发测试
     */
    @Test
    void testQryOweFee() {
        // 每个请求结果保存的路径
        String singlePath = TEST_DIR + "qryowefee/single/";
        // 合并结果保存的路径
        String resultPath = TEST_DIR + "qryowefee/";
        // 创建文件夹
        try {
            Files.createDirectories(Paths.get(singlePath));
            Files.createDirectories(Paths.get(resultPath));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        resultPath = resultPath + "欠费接口整合" + poolSize + "个线程8000条数据测试结果.csv";
        // 接口地址
        String url = "http://192.168.50.3:12091/domainB/api/acting/qryowefee/";
        Set<String> phones = Utils.readPhones();
        for (String t : phones) {
            QryowefeeDTO dto = new QryowefeeDTO("test", t);
            executor.submit(new Task(template, t, url, dto, singlePath));
        }

        // 等待所有任务执行结束
        while (executor.getActiveCount() > 0) {
        }
        mergeResult(phones, resultPath, singlePath);
    }

    /**
     * 路由查询并发测试
     */
    @Test
    void testRoute() {
        // 每个请求结果保存的路径
        String singlePath = TEST_DIR + "route/single/";
        // 合并结果保存的路径
        String resultPath = TEST_DIR + "route/";
        // 创建文件夹
        try {
            Files.createDirectories(Paths.get(singlePath));
            Files.createDirectories(Paths.get(resultPath));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        resultPath = resultPath + "路由查询10个线程10000条数据测试结果.csv";
//        resultPath = resultPath + "路由查询10个线程1000条数据测试结果.csv";
//        resultPath = resultPath + "路由查询10个线程100条数据测试结果.csv";
        // 接口地址
        String url = "http://133.96.94.108:12091/domainB/api/route/";
        Set<String> phones = Utils.readPhones();
        for (String t : phones) {
            String[] a = t.split(",");
            String phoneNumber = a[0];
            RouteDTO dto = new RouteDTO("test", phoneNumber);
            executor.submit(new Task(template, phoneNumber, url, dto, singlePath));
        }
        // 等待所有任务执行结束
        while (executor.getActiveCount() > 0) {
        }
        mergeResult(phones, resultPath, singlePath);
    }

    /**
     * 5GSA查询并发测试
     */
    @Test
    void testSa() {
        // 每个请求结果保存的路径
        String singlePath = TEST_DIR + "sa/single/";
        // 合并结果保存的路径
        String resultPath = TEST_DIR + "sa/";
        // 创建文件夹
        try {
            Files.createDirectories(Paths.get(singlePath));
            Files.createDirectories(Paths.get(resultPath));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        resultPath = resultPath + "5GSA查询10个线程10000条数据测试结果.csv";
//        resultPath = resultPath + "5GSA查询10个线程1000条数据测试结果.csv";
//        resultPath = resultPath + "5GSA查询10个线程100条数据测试结果.csv";
        // 接口地址
        String url = "http://133.96.94.108:12091/domainB/api/sa/";
        Set<String> phones = Utils.readPhones();
        for (String t : phones) {
            String[] a = t.split(",");
            String phoneNumber = a[0];
            SaDTO dto = new SaDTO("test", phoneNumber);
            executor.submit(new Task(template, phoneNumber, url, dto, singlePath));
        }
        // 等待所有任务执行结束
        while (executor.getActiveCount() > 0) {
        }
        mergeResult(phones, resultPath, singlePath);
    }

    /**
     * 功能查询并发测试
     */
    @Test
    void testSaAndVolte() {
        // 每个请求结果保存的路径
        String singlePath = TEST_DIR + "saAndVolte/single/";
        // 合并结果保存的路径
        String resultPath = TEST_DIR + "saAndVolte/";
        // 创建文件夹
        try {
            Files.createDirectories(Paths.get(singlePath));
            Files.createDirectories(Paths.get(resultPath));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        resultPath = resultPath + "功能查询10个线程10000条数据测试结果.csv";
//        resultPath = resultPath + "功能查询10个线程1000条数据测试结果.csv";
//        resultPath = resultPath + "功能查询10个线程100条数据测试结果.csv";
        // 接口地址
        String url = "http://133.96.94.108:12091/domainB/api/product/saAndVolte/";
        Set<String> phones = Utils.readPhones();
        for (String t : phones) {
            String[] a = t.split(",");
            String phoneNumber = a[0];
            SaAndVolteDTO dto = new SaAndVolteDTO("test", phoneNumber);
            executor.submit(new Task(template, phoneNumber, url, dto, singlePath));
        }
        // 等待所有任务执行结束
        while (executor.getActiveCount() > 0) {
        }
        mergeResult(phones, resultPath, singlePath);
    }

    /**
     * 用户资料三户返回并发测试
     */
    @Test
    void testUserState() {
        // 每个请求结果保存的路径
        String singlePath = TEST_DIR + "userState/single/";
        // 合并结果保存的路径
        String resultPath = TEST_DIR + "userState/";
        // 创建文件夹
        try {
            Files.createDirectories(Paths.get(singlePath));
            Files.createDirectories(Paths.get(resultPath));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        resultPath = resultPath + "用户资料三户返回10个线程7月1-24日移网质量投诉业务号码测试结果.csv";
        // 接口地址
        String url = "http://133.96.94.108:12091/domainB/api/threePart/userState/";
        Set<String> phones = Utils.readPhones();
        for (String t : phones) {
            String[] a = t.split(",");
            String phoneNumber = a[0];
            UserStateDTO dto = new UserStateDTO("test", phoneNumber);
            executor.submit(new Task(template, phoneNumber, url, dto, singlePath));
        }
        // 等待所有任务执行结束
        while (executor.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 phone : phones) {
//                phone = phone.split(",")[0];
                Path path = Paths.get(singlePath + phone + ".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("{} -> {}", phone, 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() {
            String filePath = singlePath + phoneNumber + ".csv";
            LocalDateTime startTime = LocalDateTime.now();
            long startTimestamp = System.currentTimeMillis();
            R r;
            try {
                r = restTemplate.postForObject(url, dto, R.class);
            } catch (Exception e) {
                long endTimestamp = System.currentTimeMillis();
                long cost = endTimestamp - startTimestamp;
                log.error("{} cost {} -> {}", phoneNumber, cost, e.getMessage(), e);
                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(e.getMessage()));
                    return;
                } catch (IOException ioException) {
                    log.error("{} cost {} -> {}", phoneNumber, cost, ioException.getMessage(), ioException);
                    return;
                }
            }
            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(r));
            } catch (IOException e) {
                log.error("{} cost {} -> {}", phoneNumber, cost, e.getMessage(), e);
            }
        }
    }
}

