package org.wcs.serve.service.impl;

import com.alibaba.fastjson2.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.wcs.constant.enums.database.StackerLocationStatusEnum;
import org.wcs.constant.enums.database.StockSingleTaskStatusEnum;
import org.wcs.constant.enums.database.StockSingleTaskTypeEnum;
import org.wcs.factory.AppServeResponseFactory;
import org.wcs.helper.HttpResponseHelper;
import org.wcs.helper.SseHelper;
import org.wcs.mapper.intf.*;
import org.wcs.model.po.app.*;
import org.wcs.model.vo.custom.BaseChartsDataItem;
import org.wcs.model.vo.custom.MultilineLineChartData;
import org.wcs.model.vo.custom.MultilineLineChartDataItem;
import org.wcs.model.vo.serve.AppServeDataResponse;
import org.wcs.model.vo.serve.sys.*;
import org.wcs.serve.service.intf.ISystemService;
import org.wcs.utils.AppStringUtils;
import org.wcs.utils.AppTimeUtils;
import org.wcs.utils.AppUUIDUtils;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.FileSystem;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.FormatUtil;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.InetAddress;
import java.time.LocalDate;
import java.util.*;

/**
 * 系统数据服务
 */
@Service
@RequiredArgsConstructor
public class SystemService implements ISystemService {

    private final long startTime = System.currentTimeMillis();
    private final HttpResponseHelper httpResponseHelper;
    private final SseHelper sseHelper;
    private final AppStackerLocationService stackerLocationService;
    private final AppStockSingleTaskService stockSingleTaskService;
    private final AppStockSingleTaskBakService stockSingleTaskBakService;
    private final AppRecordErrService appRecordErrService;

    /**
     * 查询系统信息
     * @param request 请求
     * @return 系统信息
     */
    @Override
    public AppServeDataResponse<QuerySystemInfoVo> querySystemInfo(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String response = httpResponseHelper.getResponse(requestURI);
        if(AppStringUtils.isNotEmpty(response)) {
            AppServeDataResponse<QuerySystemInfoVo> querySystemInfoVoAppServeDataResponse = JSONObject.parseObject(response, new TypeReference<>(){});
            return AppServeResponseFactory.success(querySystemInfoVoAppServeDataResponse.getData());
        }
        QuerySystemInfoVo querySystemInfoVo = new QuerySystemInfoVo(); // 要返回的数据
        // 运行时长
        long uptimeMillis = System.currentTimeMillis() - startTime;
        querySystemInfoVo.setSystemRunningTime(AppTimeUtils.formatDuration(uptimeMillis));

        SystemInfo si = new SystemInfo();
        HardwareAbstractionLayer hal = si.getHardware();
        OperatingSystem os = si.getOperatingSystem();

        // CPU 信息
        CentralProcessor processor = hal.getProcessor();
        Map<String, Object> cpu = new LinkedHashMap<>();
        int coreCount = processor.getLogicalProcessorCount();
        long[] ticks1 = processor.getSystemCpuLoadTicks();
        try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
        long[] ticks2 = processor.getSystemCpuLoadTicks();

        long user = ticks2[CentralProcessor.TickType.USER.getIndex()] - ticks1[CentralProcessor.TickType.USER.getIndex()];
        long system = ticks2[CentralProcessor.TickType.SYSTEM.getIndex()] - ticks1[CentralProcessor.TickType.SYSTEM.getIndex()];
        long idle = ticks2[CentralProcessor.TickType.IDLE.getIndex()] - ticks1[CentralProcessor.TickType.IDLE.getIndex()];
        long total = Arrays.stream(ticks2).sum() - Arrays.stream(ticks1).sum();
        CpuDataVo cpuDataVo = new CpuDataVo();
        cpuDataVo.setCpuCores(coreCount);
        cpuDataVo.setUserUseRatio(String.format("%.2f", user * 100.0 / total));
        cpuDataVo.setSystemUseRatio(String.format("%.2f", system * 100.0 / total));
        cpuDataVo.setTotalUseRatio(String.format("%.2f", (user + system) * 100.0 / total));
        cpuDataVo.setFreeRatio(String.format("%.2f", idle * 100.0 / total));
        querySystemInfoVo.setCpuData(cpuDataVo);

        // 内存信息
        GlobalMemory memory = hal.getMemory();
        Map<String, Object> mem = new LinkedHashMap<>();
        long totalMem = memory.getTotal();
        long available = memory.getAvailable();
        long used = totalMem - available;
        MemoryDataVo memoryDataVo = new MemoryDataVo();
        memoryDataVo.setTotal(FormatUtil.formatBytes(totalMem));
        memoryDataVo.setUsed(FormatUtil.formatBytes(used));
        memoryDataVo.setFree(FormatUtil.formatBytes(available));
        memoryDataVo.setUsage(String.format("%.2f", used * 100.0 / totalMem));
        querySystemInfoVo.setMemoryData(memoryDataVo);

        // JVM 信息
        RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        Map<String, Object> jvm = new LinkedHashMap<>();
        JvmDataVo jvmDataVo = new JvmDataVo();
        jvmDataVo.setJvmName(runtime.getVmName());
        jvmDataVo.setJavaVersion(runtime.getVmVersion());
        jvmDataVo.setStartTime(FormatUtil.formatElapsedSecs(runtime.getStartTime()));
        jvmDataVo.setRunTime(FormatUtil.formatElapsedSecs(runtime.getUptime()));
        jvmDataVo.setJvmPath(System.getProperty("user.dir"));
        jvmDataVo.setStartPath(System.getProperty("java.class.path"));
        jvmDataVo.setStartArgs(runtime.getInputArguments());
        querySystemInfoVo.setJvmData(jvmDataVo);

        // 服务器信息
        ServeDataVo serveDataVo = new ServeDataVo();
        try {
            serveDataVo.setServeName(InetAddress.getLocalHost().getHostName());
            serveDataVo.setServeIp(InetAddress.getLocalHost().getHostAddress());
        } catch (Exception e) {
            serveDataVo.setServeName("未知");
            serveDataVo.setServeIp("未知");
        }
        serveDataVo.setSystemName( os.toString());
        serveDataVo.setSystemVersion(System.getProperty("os.arch"));
        querySystemInfoVo.setServeData(serveDataVo);

        // 磁盘信息
        FileSystem fileSystem = os.getFileSystem();
        List<OSFileStore> fileStores = fileSystem.getFileStores();
        List<DiskDataVo> diskDataVos = new ArrayList<>();
        for (OSFileStore fs : fileStores) {
            DiskDataVo disk = new DiskDataVo();
            disk.setDiskPath(fs.getMount());
            disk.setDiskType(fs.getType());
            disk.setTotalSize(FormatUtil.formatBytes(fs.getTotalSpace()));
            disk.setFreeSize(FormatUtil.formatBytes(fs.getUsableSpace()));
            diskDataVos.add(disk);
        }
        querySystemInfoVo.setDiskData(diskDataVos);
        AppServeDataResponse<QuerySystemInfoVo> successResponse = AppServeResponseFactory.success(querySystemInfoVo);
        httpResponseHelper.setResponse(requestURI, AppStringUtils.objectToString(successResponse), 5);
        return successResponse;
    }

    /**
     * 创建信息连接
     * @return SSE
     */
    @Override
    public SseEmitter acceptInfo() {
        String userId = AppUUIDUtils.getNewUUID();
        SseEmitter emitter = new SseEmitter(1000L * 60 * 5);
        emitter.onTimeout(() -> sseHelper.removeEmitter(userId));
        emitter.onCompletion(() -> sseHelper.removeEmitter(userId));
        sseHelper.setEmitter(userId, emitter);
        return emitter;
    }


    /**
     * 查询库存使用情况
     * @return 库存使用情况
     */
    @Override
    public AppServeDataResponse<List<BaseChartsDataItem>> queryStockUsage(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String response = httpResponseHelper.getResponse(requestURI);
        if(AppStringUtils.isNotEmpty(response)) {
            AppServeDataResponse<List<BaseChartsDataItem>> querySystemInfoVoAppServeDataResponse = JSONObject.parseObject(response, new TypeReference<>(){});
            return AppServeResponseFactory.success(querySystemInfoVoAppServeDataResponse.getData());
        }
        List<AppStackerLocation> stackerLocations = stackerLocationService.queryStackerLocationAll();
        if(stackerLocations == null) {
            return AppServeResponseFactory.fail("数据服务异常", null);
        }
        List<BaseChartsDataItem> baseChartsDataItems = new ArrayList<>(); // 要返回的结果
        long emptyCount = stackerLocations.stream().filter(stackerLocation -> Objects.equals(stackerLocation.getLocationStatus(), StackerLocationStatusEnum.EMPTY.getCode())).count();
        baseChartsDataItems.add(new BaseChartsDataItem("空闲",emptyCount));
        baseChartsDataItems.add(new BaseChartsDataItem("不可用", (long) stackerLocations.size() - emptyCount));
        AppServeDataResponse<List<BaseChartsDataItem>> successResponse = AppServeResponseFactory.success(baseChartsDataItems);
        httpResponseHelper.setResponse(requestURI, AppStringUtils.objectToString(successResponse), 10);
        return successResponse;
    }

    /**
     * 查询今日入库出库数量
     * @return 今日入库出库数量
     */
    @Override
    public AppServeDataResponse<List<BaseChartsDataItem>> queryTodayInOutCount(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String response = httpResponseHelper.getResponse(requestURI);
        if(AppStringUtils.isNotEmpty(response)) {
            AppServeDataResponse<List<BaseChartsDataItem>> querySystemInfoVoAppServeDataResponse = JSONObject.parseObject(response, new TypeReference<>(){});
            return AppServeResponseFactory.success(querySystemInfoVoAppServeDataResponse.getData());
        }
        List<AppStockSingleTaskBak> stockSingleTasks = stockSingleTaskBakService.queryTaskRecentDays(1, StockSingleTaskStatusEnum.COMPLETE.getCode());
        if(stockSingleTasks == null) {
            return AppServeResponseFactory.fail("数据服务异常", null);
        }
        List<BaseChartsDataItem> baseChartsDataItems = new ArrayList<>();
        int inCount = 0;
        int outCount = 0;
        int moveCount = 0;
        for (AppStockSingleTaskBak stockSingleTask : stockSingleTasks) {
            if(Objects.equals(stockSingleTask.getTaskType(), StockSingleTaskTypeEnum.STACKER_IN.getCode())) {
                inCount++;
                continue;
            }
            if(Objects.equals(stockSingleTask.getTaskType(), StockSingleTaskTypeEnum.STACKER_OUT.getCode())) {
                outCount++;
                continue;
            }
            if(Objects.equals(stockSingleTask.getTaskType(), StockSingleTaskTypeEnum.STACKER_MOVE.getCode())) {
                moveCount++;
            }
        }
        baseChartsDataItems.add(new BaseChartsDataItem("入库", (long) inCount));
        baseChartsDataItems.add(new BaseChartsDataItem("出库", (long) outCount));
        baseChartsDataItems.add(new BaseChartsDataItem("移库", (long) moveCount));
        AppServeDataResponse<List<BaseChartsDataItem>> successResponse = AppServeResponseFactory.success(baseChartsDataItems);
        httpResponseHelper.setResponse(requestURI, AppStringUtils.objectToString(successResponse), 10);
        return successResponse;
    }

    /**
     * 获取近7天设备错误数量
     * @return 近7天设备错误数量
     */
    @Override
    public AppServeDataResponse<List<BaseChartsDataItem>> queryDeviceErrorCountDay7(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String response = httpResponseHelper.getResponse(requestURI);
        if(AppStringUtils.isNotEmpty(response)) {
            AppServeDataResponse<List<BaseChartsDataItem>> querySystemInfoVoAppServeDataResponse = JSONObject.parseObject(response, new TypeReference<>(){});
            return AppServeResponseFactory.success(querySystemInfoVoAppServeDataResponse.getData());
        }
        List<AppRecordErr> recordErrors = appRecordErrService.queryRecordErrorDetailRecentDays(7);
        if(recordErrors == null) {
            return AppServeResponseFactory.fail("数据服务异常", null);
        }
        List<BaseChartsDataItem> baseChartsDataItems = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            LocalDate now = LocalDate.now();
            LocalDate localDate = now.minusDays(i);
            long count = recordErrors.stream().filter(recordError -> recordError.getCreateTime().toLocalDate().equals(localDate)).count();
            baseChartsDataItems.add(new BaseChartsDataItem(AppTimeUtils.formatLocalDatetime(localDate, "MM/dd"), count));
        }
        AppServeDataResponse<List<BaseChartsDataItem>> successResponse = AppServeResponseFactory.success(baseChartsDataItems);
        httpResponseHelper.setResponse(requestURI, AppStringUtils.objectToString(successResponse), 60);
        return successResponse;
    }

    /**
     * 获取近7天入库出库数量
     * @return 近7天入库出库数量
     */
    @Override
    public AppServeDataResponse<MultilineLineChartData> queryInOutCountDay7(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String response = httpResponseHelper.getResponse(requestURI);
        if(AppStringUtils.isNotEmpty(response)) {
            AppServeDataResponse<MultilineLineChartData> querySystemInfoVoAppServeDataResponse = JSONObject.parseObject(response, new TypeReference<>(){});
            return AppServeResponseFactory.success(querySystemInfoVoAppServeDataResponse.getData());
        }
        List<AppStockSingleTaskBak> stockSingleTasks = stockSingleTaskBakService.queryTaskRecentDays(7,StockSingleTaskStatusEnum.COMPLETE.getCode());
        if(stockSingleTasks == null) {
            return AppServeResponseFactory.fail("数据服务异常", null);
        }
        MultilineLineChartData multilineLineChartData = new MultilineLineChartData();
        List<String> xDataList = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            LocalDate now = LocalDate.now();
            LocalDate localDate = now.minusDays(i);
            xDataList.add(AppTimeUtils.formatLocalDatetime(localDate, "MM/dd"));
        }
        List<MultilineLineChartDataItem> yDataList = new ArrayList<>();
        // 入库数据
        List<Integer> inCountList = new ArrayList<>();
        for (String xData : xDataList) {
            long count = stockSingleTasks.stream().filter(stockSingleTask ->
                    AppTimeUtils.formatLocalDatetime(stockSingleTask.getCreateTime().toLocalDate(), "MM/dd").equals(xData)
                            && Objects.equals(stockSingleTask.getTaskType(), StockSingleTaskTypeEnum.STACKER_IN.getCode())).count();
            inCountList.add((int) count);
        }
        yDataList.add(new MultilineLineChartDataItem("入库", inCountList));
        // 出库数据
        List<Integer> outCountList = new ArrayList<>();
        for (String xData : xDataList) {
            long count = stockSingleTasks.stream().filter(stockSingleTask ->
                    AppTimeUtils.formatLocalDatetime(stockSingleTask.getCreateTime().toLocalDate(), "MM/dd").equals(xData)
                            && Objects.equals(stockSingleTask.getTaskType(), StockSingleTaskTypeEnum.STACKER_OUT.getCode())).count();
            outCountList.add((int) count);
        }
        yDataList.add(new MultilineLineChartDataItem("出库", outCountList));
        // 移库数据
        List<Integer> moveCountList = new ArrayList<>();
        for (String xData : xDataList) {
            long count = stockSingleTasks.stream().filter(stockSingleTask ->
                    AppTimeUtils.formatLocalDatetime(stockSingleTask.getCreateTime().toLocalDate(), "MM/dd").equals(xData)
                            && Objects.equals(stockSingleTask.getTaskType(), StockSingleTaskTypeEnum.STACKER_MOVE.getCode())).count();
            moveCountList.add((int) count);
        }
        yDataList.add(new MultilineLineChartDataItem("移库", moveCountList));
        multilineLineChartData.setXDataList(xDataList);
        multilineLineChartData.setYDataList(yDataList);
        AppServeDataResponse<MultilineLineChartData> successResponse = AppServeResponseFactory.success(multilineLineChartData);
        httpResponseHelper.setResponse(requestURI, AppStringUtils.objectToString(successResponse), 60);
        return successResponse;
    }

}
