package org.framework.lazy.cloud.network.heartbeat.server.standalone.application.impl;

import jakarta.annotation.Resource;
import org.framework.lazy.cloud.network.heartbeat.server.properties.ServerNodeProperties;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.LazyVisitorPortPerDayFlowApplication;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.assembler.VisitorPortPerDayFlowDTOAssembler;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.visitor.port.per.day.flow.*;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyClientFlowPerDayEchartsDTO;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyClientPortFlowPerDayEchartsDTO;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyVisitorPortPerDayFlowDTO;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.state.LazyNettyClientState;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.state.LazyNettyClientStateRepository;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.visitor.port.per.day.flow.LazyVisitorPortPerDayFlow;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.visitor.port.per.day.flow.LazyVisitorPortPerDayFlowRepository;
import org.wu.framework.core.utils.ObjectUtils;
import org.wu.framework.database.lazy.web.plus.stereotype.LazyApplication;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * describe 每日统计流量
 *
 * @author Jia wei Wu
 * @date 2024/03/19 09:53 上午
 * @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyApplicationImpl
 **/
@LazyApplication
public class LazyVisitorPortPerDayFlowApplicationImpl implements LazyVisitorPortPerDayFlowApplication {

    @Resource
    LazyVisitorPortPerDayFlowRepository lazyVisitorPortPerDayFlowRepository;

    @Resource
    LazyNettyClientStateRepository lazyNettyClientStateRepository;

    @Resource
    ServerNodeProperties serverNodeProperties;

    /**
     * describe 新增每日统计流量
     *
     * @param lazyVisitorPortPerDayFlowStoryCommand 新增每日统计流量
     * @return {@link Result<  LazyVisitorPortPerDayFlow  >} 每日统计流量新增后领域对象
     * @author Jia wei Wu
     * @date 2024/03/19 09:53 上午
     **/

    @Override
    public Result<LazyVisitorPortPerDayFlow> story(LazyVisitorPortPerDayFlowStoryCommand lazyVisitorPortPerDayFlowStoryCommand) {
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = VisitorPortPerDayFlowDTOAssembler.INSTANCE.toVisitorPortPerDayFlow(lazyVisitorPortPerDayFlowStoryCommand);

        String serverId = serverNodeProperties.getNodeId();
        lazyVisitorPortPerDayFlow.setServerId(serverId);
        return lazyVisitorPortPerDayFlowRepository.story(lazyVisitorPortPerDayFlow);
    }

    /**
     * describe 批量新增每日统计流量
     *
     * @param lazyVisitorPortPerDayFlowStoryCommandList 批量新增每日统计流量
     * @return {@link Result<List< LazyVisitorPortPerDayFlow >>} 每日统计流量新增后领域对象集合
     * @author Jia wei Wu
     * @date 2024/03/19 09:53 上午
     **/

    @Override
    public Result<List<LazyVisitorPortPerDayFlow>> batchStory(List<LazyVisitorPortPerDayFlowStoryCommand> lazyVisitorPortPerDayFlowStoryCommandList) {
        List<LazyVisitorPortPerDayFlow> lazyVisitorPortPerDayFlowList = lazyVisitorPortPerDayFlowStoryCommandList
                .stream()
                .map(lazyVisitorPortPerDayFlowStoryCommand -> {
                            LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = VisitorPortPerDayFlowDTOAssembler.INSTANCE.toVisitorPortPerDayFlow(lazyVisitorPortPerDayFlowStoryCommand);
                    String serverId = serverNodeProperties.getNodeId();

                            lazyVisitorPortPerDayFlow.setServerId(serverId);
                            return lazyVisitorPortPerDayFlow;
                        }
                )
                .collect(Collectors.toList());
        return lazyVisitorPortPerDayFlowRepository.batchStory(lazyVisitorPortPerDayFlowList);
    }

    /**
     * describe 更新每日统计流量
     *
     * @param lazyVisitorPortPerDayFlowUpdateCommand 更新每日统计流量
     * @return {@link Result< LazyVisitorPortPerDayFlow >} 每日统计流量领域对象
     * @author Jia wei Wu
     * @date 2024/03/19 09:53 上午
     **/

    @Override
    public Result<LazyVisitorPortPerDayFlow> updateOne(LazyVisitorPortPerDayFlowUpdateCommand lazyVisitorPortPerDayFlowUpdateCommand) {
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = VisitorPortPerDayFlowDTOAssembler.INSTANCE.toVisitorPortPerDayFlow(lazyVisitorPortPerDayFlowUpdateCommand);
        String serverId = serverNodeProperties.getNodeId();

        lazyVisitorPortPerDayFlow.setServerId(serverId);  return lazyVisitorPortPerDayFlowRepository.story(lazyVisitorPortPerDayFlow);
    }

    /**
     * describe 查询单个每日统计流量
     *
     * @param lazyVisitorPortPerDayFlowQueryOneCommand 查询单个每日统计流量
     * @return {@link Result<  LazyVisitorPortPerDayFlowDTO  >} 每日统计流量DTO对象
     * @author Jia wei Wu
     * @date 2024/03/19 09:53 上午
     **/

    @Override
    public Result<LazyVisitorPortPerDayFlowDTO> findOne(LazyVisitorPortPerDayFlowQueryOneCommand lazyVisitorPortPerDayFlowQueryOneCommand) {
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = VisitorPortPerDayFlowDTOAssembler.INSTANCE.toVisitorPortPerDayFlow(lazyVisitorPortPerDayFlowQueryOneCommand);
        String serverId = serverNodeProperties.getNodeId();

        lazyVisitorPortPerDayFlow.setServerId(serverId);  return lazyVisitorPortPerDayFlowRepository.findOne(lazyVisitorPortPerDayFlow).convert(VisitorPortPerDayFlowDTOAssembler.INSTANCE::fromVisitorPortPerDayFlow);
    }

    /**
     * describe 查询多个每日统计流量
     *
     * @param lazyVisitorPortPerDayFlowQueryListCommand 查询多个每日统计流量
     * @return {@link Result<List< LazyVisitorPortPerDayFlowDTO >>} 每日统计流量DTO对象
     * @author Jia wei Wu
     * @date 2024/03/19 09:53 上午
     **/

    @Override
    public Result<List<LazyVisitorPortPerDayFlowDTO>> findList(LazyVisitorPortPerDayFlowQueryListCommand lazyVisitorPortPerDayFlowQueryListCommand) {
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = VisitorPortPerDayFlowDTOAssembler.INSTANCE.toVisitorPortPerDayFlow(lazyVisitorPortPerDayFlowQueryListCommand);
        String serverId = serverNodeProperties.getNodeId();

        lazyVisitorPortPerDayFlow.setServerId(serverId); return lazyVisitorPortPerDayFlowRepository.findList(lazyVisitorPortPerDayFlow).convert(visitorPortPerDayFlows -> visitorPortPerDayFlows.stream().map(VisitorPortPerDayFlowDTOAssembler.INSTANCE::fromVisitorPortPerDayFlow).collect(Collectors.toList()));
    }

    /**
     * describe 分页查询多个每日统计流量
     *
     * @param lazyVisitorPortPerDayFlowQueryListCommand 分页查询多个每日统计流量
     * @return {@link Result<LazyPage< LazyVisitorPortPerDayFlowDTO >>} 分页每日统计流量DTO对象
     * @author Jia wei Wu
     * @date 2024/03/19 09:53 上午
     **/

    @Override
    public Result<LazyPage<LazyVisitorPortPerDayFlowDTO>> findPage(int size, int current, LazyVisitorPortPerDayFlowQueryListCommand lazyVisitorPortPerDayFlowQueryListCommand) {
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = VisitorPortPerDayFlowDTOAssembler.INSTANCE.toVisitorPortPerDayFlow(lazyVisitorPortPerDayFlowQueryListCommand);
        String serverId = serverNodeProperties.getNodeId();

        lazyVisitorPortPerDayFlow.setServerId(serverId);  return lazyVisitorPortPerDayFlowRepository.findPage(size, current, lazyVisitorPortPerDayFlow).convert(page -> page.convert(VisitorPortPerDayFlowDTOAssembler.INSTANCE::fromVisitorPortPerDayFlow));
    }

    /**
     * describe 删除每日统计流量
     *
     * @param lazyVisitorPortPerDayFlowRemoveCommand 删除每日统计流量
     * @return {@link Result< LazyVisitorPortPerDayFlow >} 每日统计流量
     * @author Jia wei Wu
     * @date 2024/03/19 09:53 上午
     **/

    @Override
    public Result<LazyVisitorPortPerDayFlow> remove(LazyVisitorPortPerDayFlowRemoveCommand lazyVisitorPortPerDayFlowRemoveCommand) {
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = VisitorPortPerDayFlowDTOAssembler.INSTANCE.toVisitorPortPerDayFlow(lazyVisitorPortPerDayFlowRemoveCommand);
        String serverId = serverNodeProperties.getNodeId();

        lazyVisitorPortPerDayFlow.setServerId(serverId); return lazyVisitorPortPerDayFlowRepository.remove(lazyVisitorPortPerDayFlow);
    }

    /**
     * 获取客户近七天流量数据
     *
     * @return
     */
    @Override
    public Result<LazyClientPortFlowPerDayEchartsDTO> findClient7DayFlow() {

        int dayLength = 7;

        LazyNettyClientState lazyNettyClientState = new LazyNettyClientState();
        String serverId = serverNodeProperties.getNodeId();

        lazyNettyClientState.setServerId(serverId);
        return findClientDayFlow(dayLength, null);
    }

    /**
     * 获取客户流量数据
     *
     * @param dayLength 天数
     * @param clientIds 客户端
     * @return
     */

    @Override
    public Result<LazyClientPortFlowPerDayEchartsDTO> findClientDayFlow(int dayLength, List<String> clientIds) {
        LazyClientPortFlowPerDayEchartsDTO lazyClientPortFlowPerDayEchartsDTO = new LazyClientPortFlowPerDayEchartsDTO();

        // 流量日期
        List<String> dayList = Stream.iterate(1, n -> n + 1).limit(dayLength).map(index -> {
            LocalDate minus = LocalDate.now().minusDays(dayLength - index);
            return minus.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }).toList();
        lazyClientPortFlowPerDayEchartsDTO.setDayList(dayList);
        LazyNettyClientState lazyNettyClientState = new LazyNettyClientState();
        String serverId = serverNodeProperties.getNodeId();

        lazyNettyClientState.setServerId(serverId);
        // 客户端ID
        if (ObjectUtils.isEmpty(clientIds)) {
            // 客户端ID
            clientIds = lazyNettyClientStateRepository.findList(lazyNettyClientState)
                    .applyOther(nettyClientStates -> nettyClientStates
                            .stream().map(LazyNettyClientState::getClientId)
                            .toList());
        }
        lazyClientPortFlowPerDayEchartsDTO.setClientIdList(clientIds);
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow1 = new LazyVisitorPortPerDayFlow();
        lazyVisitorPortPerDayFlow1.setServerId(serverId);
        List<LazyVisitorPortPerDayFlow> lazyVisitorPortPerDayFlowList = lazyVisitorPortPerDayFlowRepository.findList(lazyVisitorPortPerDayFlow1)
                .applyOther(clientPerDayFlows -> clientPerDayFlows);

        if (ObjectUtils.isEmpty(lazyVisitorPortPerDayFlowList)) {
            return ResultFactory.successOf();
        }
        List<List<LazyVisitorPortPerDayFlow>> collect = lazyVisitorPortPerDayFlowList
                .stream()
                .collect(Collectors
                        .groupingBy(
                                LazyVisitorPortPerDayFlow::getClientId))// 客户端分组
                .values()
                .stream()
                .map(
                        visitorPortPerDayFlows -> visitorPortPerDayFlows
                                .stream()
                                .collect(Collectors.groupingBy(LazyVisitorPortPerDayFlow::getVisitorPort)) // 访客分组
                                .values()
                                .stream()
                                .toList()
                )
                .collect(Collectors.flatMapping(
                        Collection::stream,
                        Collectors.toList()));


        // in flow
        List<LazyClientPortFlowPerDayEchartsDTO.ClientFlow> clientInFlowList = collect.stream().map(visitorPortPerDayFlows -> {
            LazyClientPortFlowPerDayEchartsDTO.ClientFlow clientFlow = new LazyClientPortFlowPerDayEchartsDTO.ClientFlow();
            LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = visitorPortPerDayFlows.stream().findFirst().get();
            String clientId = lazyVisitorPortPerDayFlow.getClientId();
            Integer visitorPort = lazyVisitorPortPerDayFlow.getVisitorPort();
            clientFlow.setClientId(clientId);
            clientFlow.setVisitorPort(visitorPort);
            Map<String, Integer> portPerDayFlowMap = visitorPortPerDayFlows.stream()
                    .collect(Collectors.toMap(LazyVisitorPortPerDayFlow::getDay, LazyVisitorPortPerDayFlow::getInFlow, (A, B) -> A));
            List<Integer> flowList = dayList.stream().map(day -> portPerDayFlowMap.getOrDefault(day, 0)).toList();
            clientFlow.setFlowList(flowList);
            return clientFlow;
        }).toList();


        lazyClientPortFlowPerDayEchartsDTO.setClientInFlowList(clientInFlowList);


        // out flow
        List<LazyClientPortFlowPerDayEchartsDTO.ClientFlow> clientOutFlowList = collect.stream().map(visitorPortPerDayFlows -> {
            LazyClientPortFlowPerDayEchartsDTO.ClientFlow clientFlow = new LazyClientPortFlowPerDayEchartsDTO.ClientFlow();
            LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow = visitorPortPerDayFlows.stream().findFirst().get();
            String clientId = lazyVisitorPortPerDayFlow.getClientId();
            Integer visitorPort = lazyVisitorPortPerDayFlow.getVisitorPort();
            clientFlow.setClientId(clientId);
            clientFlow.setVisitorPort(visitorPort);
            Map<String, Integer> portPerDayFlowMap = visitorPortPerDayFlows.stream()
                    .collect(Collectors.toMap(LazyVisitorPortPerDayFlow::getDay, LazyVisitorPortPerDayFlow::getOutFlow, (A, B) -> A));
            List<Integer> flowList = dayList.stream().map(day -> portPerDayFlowMap.getOrDefault(day, 0)).toList();
            clientFlow.setFlowList(flowList);
            return clientFlow;
        }).toList();

        lazyClientPortFlowPerDayEchartsDTO.setClientOutFlowList(clientOutFlowList);


        //
        return ResultFactory.successOf(lazyClientPortFlowPerDayEchartsDTO);
    }

    /**
     * 获取客户流量数据
     *
     * @return
     */
    @Override
    public Result<LazyClientFlowPerDayEchartsDTO> findClientPerDayFlow() {
        LazyClientFlowPerDayEchartsDTO lazyClientFlowPerDayEchartsDTO = new LazyClientFlowPerDayEchartsDTO();
        LazyVisitorPortPerDayFlow lazyVisitorPortPerDayFlow1 = new LazyVisitorPortPerDayFlow();
        String serverId = serverNodeProperties.getNodeId();
        lazyVisitorPortPerDayFlow1.setServerId(serverId);
        List<LazyVisitorPortPerDayFlow> lazyVisitorPortPerDayFlowList =
                lazyVisitorPortPerDayFlowRepository.findList(lazyVisitorPortPerDayFlow1).applyOther(clientPerDayFlows -> clientPerDayFlows)
                ;
        if(ObjectUtils.isEmpty(lazyVisitorPortPerDayFlowList)){
            return ResultFactory.successOf(lazyClientFlowPerDayEchartsDTO);
        }

        List<String> dayList = lazyVisitorPortPerDayFlowList.stream().map(LazyVisitorPortPerDayFlow::getDay).toList();

        List<Integer> clientInFlowList = lazyVisitorPortPerDayFlowList.stream().map(LazyVisitorPortPerDayFlow::getInFlow).toList();
        List<Integer> clientOutFlowList =
                lazyVisitorPortPerDayFlowList.stream().map(LazyVisitorPortPerDayFlow::getOutFlow).toList();
        List<Integer> clientAllFlowList = lazyVisitorPortPerDayFlowList.stream().map(visitorPortPerDayFlow -> ObjectUtils.isEmpty(visitorPortPerDayFlow.getInFlow()) ? 0 : visitorPortPerDayFlow.getInFlow()
                + (ObjectUtils.isEmpty(visitorPortPerDayFlow.getOutFlow()) ? 0 : visitorPortPerDayFlow.getOutFlow())).toList();

        lazyClientFlowPerDayEchartsDTO.setDayList(dayList);
        lazyClientFlowPerDayEchartsDTO.setClientInFlowList(clientInFlowList);
        lazyClientFlowPerDayEchartsDTO.setClientOutFlowList(clientOutFlowList);
        lazyClientFlowPerDayEchartsDTO.setClientAllFlowList(clientAllFlowList);
        return ResultFactory.successOf(lazyClientFlowPerDayEchartsDTO);
    }
}