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

import org.framework.lazy.cloud.network.heartbeat.common.NettyClientVisitorContext;
import org.framework.lazy.cloud.network.heartbeat.server.netty.socket.NettyServerPermeateClientVisitorSocket;
import org.framework.lazy.cloud.network.heartbeat.server.properties.ServerNodeProperties;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.LazyInternalNetworkServerPermeateClientMappingApplication;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.assembler.InternalNetworkPenetrationMappingDTOAssembler;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.server.permeate.client.mapping.*;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.server.permeate.client.mapping.LazyInternalNetworkServerPermeateClientMapping;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.server.permeate.client.mapping.LazyInternalNetworkServerPermeateClientMappingRepository;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyInternalNetworkPenetrationMappingDTO;
import org.springframework.transaction.annotation.Transactional;
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 jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyApplicationImpl;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelFlowAdapter;

import java.util.List;
import java.util.stream.Collectors;

/**
 * describe 内网穿透映射
 *
 * @author Jia wei Wu
 * @date 2023/12/29 05:21 下午
 * @see DefaultDDDLazyApplicationImpl
 **/
@Slf4j
@LazyApplication
public class LazyInternalNetworkServerPermeateClientMappingApplicationImpl implements LazyInternalNetworkServerPermeateClientMappingApplication {

    @Resource
    LazyInternalNetworkServerPermeateClientMappingRepository lazyInternalNetworkServerPermeateClientMappingRepository;

    @Resource
    ChannelFlowAdapter channelFlowAdapter;

    @Resource
    ServerNodeProperties serverNodeProperties;


    /**
     * describe 新增内网穿透映射
     *
     * @param lazyInternalNetworkServerPermeateClientMappingStoryCommand 新增内网穿透映射
     * @return {@link Result<   LazyInternalNetworkServerPermeateClientMapping   >} 内网穿透映射新增后领域对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Override
    public Result<LazyInternalNetworkServerPermeateClientMapping> story(LazyInternalNetworkServerPermeateClientMappingStoryCommand lazyInternalNetworkServerPermeateClientMappingStoryCommand) {
        LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkServerPermeateClientMappingStoryCommand);
        lazyInternalNetworkServerPermeateClientMapping.setIsDeleted(false);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkServerPermeateClientMapping.setServerId(serverId);

        String clientId = lazyInternalNetworkServerPermeateClientMapping.getClientId();
        String clientTargetIp = lazyInternalNetworkServerPermeateClientMapping.getClientTargetIp();
        Integer clientTargetPort = lazyInternalNetworkServerPermeateClientMapping.getClientTargetPort();
        Integer visitorPort = lazyInternalNetworkServerPermeateClientMapping.getVisitorPort();

        // 创建访客通道池

        this.changeSocket(clientId, clientTargetIp, clientTargetPort, visitorPort);
        return lazyInternalNetworkServerPermeateClientMappingRepository.story(lazyInternalNetworkServerPermeateClientMapping);
    }

    /**
     * describe 批量新增内网穿透映射
     *
     * @param lazyInternalNetworkServerPermeateClientMappingStoryCommandList 批量新增内网穿透映射
     * @return {@link Result<List<  LazyInternalNetworkServerPermeateClientMapping  >>} 内网穿透映射新增后领域对象集合
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Transactional
    @Override
    public Result<List<LazyInternalNetworkServerPermeateClientMapping>> batchStory(List<LazyInternalNetworkServerPermeateClientMappingStoryCommand> lazyInternalNetworkServerPermeateClientMappingStoryCommandList) {
        List<LazyInternalNetworkServerPermeateClientMapping> lazyInternalNetworkServerPermeateClientMappingList =
                lazyInternalNetworkServerPermeateClientMappingStoryCommandList
                        .stream()
                        .map(lazyInternalNetworkPenetrationMappingStoryCommand -> {
                            LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingStoryCommand);
                            String serverId = serverNodeProperties.getNodeId();
                            lazyInternalNetworkServerPermeateClientMapping.setServerId(serverId);
                            return lazyInternalNetworkServerPermeateClientMapping;
                        })
                        .collect(Collectors.toList());

        Result<List<LazyInternalNetworkServerPermeateClientMapping>> batchStory = lazyInternalNetworkServerPermeateClientMappingRepository.batchStory(lazyInternalNetworkServerPermeateClientMappingList);
        // 开启端口
        for (LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping : lazyInternalNetworkServerPermeateClientMappingList) {
            String clientId = lazyInternalNetworkServerPermeateClientMapping.getClientId();
            String clientTargetIp = lazyInternalNetworkServerPermeateClientMapping.getClientTargetIp();
            Integer clientTargetPort = lazyInternalNetworkServerPermeateClientMapping.getClientTargetPort();
            Integer visitorPort = lazyInternalNetworkServerPermeateClientMapping.getVisitorPort();
            this.changeSocket(clientId, clientTargetIp, clientTargetPort, visitorPort);
        }
        return batchStory;
    }

    /**
     * describe 更新内网穿透映射
     *
     * @param lazyInternalNetworkServerPermeateClientMappingUpdateCommand 更新内网穿透映射
     * @return {@link Result<  LazyInternalNetworkServerPermeateClientMapping  >} 内网穿透映射领域对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Transactional
    @Override
    public Result<LazyInternalNetworkServerPermeateClientMapping> updateOne(LazyInternalNetworkServerPermeateClientMappingUpdateCommand lazyInternalNetworkServerPermeateClientMappingUpdateCommand) {
        LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkServerPermeateClientMappingUpdateCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkServerPermeateClientMapping.setServerId(serverId);
        // 删除绑定数据
        Result<LazyInternalNetworkServerPermeateClientMapping> story = lazyInternalNetworkServerPermeateClientMappingRepository.story(lazyInternalNetworkServerPermeateClientMapping);
        String clientId = lazyInternalNetworkServerPermeateClientMapping.getClientId();
        String clientTargetIp = lazyInternalNetworkServerPermeateClientMapping.getClientTargetIp();
        Integer clientTargetPort = lazyInternalNetworkServerPermeateClientMapping.getClientTargetPort();
        Integer visitorPort = lazyInternalNetworkServerPermeateClientMapping.getVisitorPort();
        this.changeSocket(clientId, clientTargetIp, clientTargetPort, visitorPort);
        return story;
    }


    /**
     * 变更 网络穿透
     *
     * @param clientId         客户端ID
     * @param clientTargetIp   客户端目标IP
     * @param clientTargetPort 客户端莫表端口
     * @param visitorPort      访客端口
     */
    private void changeSocket(String clientId, String clientTargetIp, Integer clientTargetPort, Integer visitorPort) {
        // 删除 客户端映射
        this.changeCloseSocket(clientId, visitorPort);
        // 更新 客户端映射
        createVisitor(clientId, clientTargetIp, clientTargetPort, visitorPort);
    }


    /**
     * 删除 通道
     *
     * @param clientId    客户端ID
     * @param visitorPort 访客端口
     */
    private void changeCloseSocket(String clientId, Integer visitorPort) {
        // 删除 客户端映射
        List<NettyServerPermeateClientVisitorSocket> nettyServerPermeateClientVisitorSocketList = NettyClientVisitorContext.getVisitorSockets(clientId);
        if (!ObjectUtils.isEmpty(nettyServerPermeateClientVisitorSocketList)) {
            nettyServerPermeateClientVisitorSocketList = nettyServerPermeateClientVisitorSocketList.stream()
                    .filter(nettyServerPermeateClientVisitorSocket -> nettyServerPermeateClientVisitorSocket.getVisitorPort() == visitorPort).toList();
            if (!ObjectUtils.isEmpty(nettyServerPermeateClientVisitorSocketList)) {
                // 关闭端口
                for (NettyServerPermeateClientVisitorSocket nettyServerPermeateClientVisitorSocket : nettyServerPermeateClientVisitorSocketList) {
                        nettyServerPermeateClientVisitorSocket.close();

                }
            }
        }
    }

    /**
     * describe 查询单个内网穿透映射
     *
     * @param lazyInternalNetworkServerPermeateClientMappingQueryOneCommand 查询单个内网穿透映射
     * @return {@link Result<  LazyInternalNetworkPenetrationMappingDTO  >} 内网穿透映射DTO对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/
    @Override
    public Result<LazyInternalNetworkPenetrationMappingDTO> findOne(LazyInternalNetworkServerPermeateClientMappingQueryOneCommand lazyInternalNetworkServerPermeateClientMappingQueryOneCommand) {
        LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkServerPermeateClientMappingQueryOneCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkServerPermeateClientMapping.setServerId(serverId);
        return lazyInternalNetworkServerPermeateClientMappingRepository.findOne(lazyInternalNetworkServerPermeateClientMapping).convert(InternalNetworkPenetrationMappingDTOAssembler.INSTANCE::fromInternalNetworkPenetrationMapping);
    }

    /**
     * describe 查询多个内网穿透映射
     *
     * @param lazyInternalNetworkServerPermeateClientMappingQueryListCommand 查询多个内网穿透映射
     * @return {@link Result<List< LazyInternalNetworkPenetrationMappingDTO >>} 内网穿透映射DTO对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Override
    public Result<List<LazyInternalNetworkPenetrationMappingDTO>> findList(LazyInternalNetworkServerPermeateClientMappingQueryListCommand lazyInternalNetworkServerPermeateClientMappingQueryListCommand) {
        LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkServerPermeateClientMappingQueryListCommand);

        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkServerPermeateClientMapping.setServerId(serverId);
        return lazyInternalNetworkServerPermeateClientMappingRepository.findList(lazyInternalNetworkServerPermeateClientMapping).convert(internalNetworkPenetrationMappings -> internalNetworkPenetrationMappings.stream().map(InternalNetworkPenetrationMappingDTOAssembler.INSTANCE::fromInternalNetworkPenetrationMapping).collect(Collectors.toList()));
    }

    /**
     * describe 分页查询多个内网穿透映射
     *
     * @param lazyInternalNetworkServerPermeateClientMappingQueryListCommand 分页查询多个内网穿透映射
     * @return {@link Result<LazyPage< LazyInternalNetworkPenetrationMappingDTO >>} 分页内网穿透映射DTO对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Override
    public Result<LazyPage<LazyInternalNetworkPenetrationMappingDTO>> findPage(int size, int current, LazyInternalNetworkServerPermeateClientMappingQueryListCommand lazyInternalNetworkServerPermeateClientMappingQueryListCommand) {
        LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkServerPermeateClientMappingQueryListCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkServerPermeateClientMapping.setServerId(serverId);
        return lazyInternalNetworkServerPermeateClientMappingRepository.findPage(size, current, lazyInternalNetworkServerPermeateClientMapping).convert(page -> page.convert(InternalNetworkPenetrationMappingDTOAssembler.INSTANCE::fromInternalNetworkPenetrationMapping));
    }

    /**
     * describe 删除内网穿透映射
     *
     * @param lazyInternalNetworkServerPermeateClientMappingRemoveCommand 删除内网穿透映射
     * @return {@link Result<  LazyInternalNetworkServerPermeateClientMapping  >} 内网穿透映射
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Transactional
    @Override
    public Result<LazyInternalNetworkServerPermeateClientMapping> remove(LazyInternalNetworkServerPermeateClientMappingRemoveCommand lazyInternalNetworkServerPermeateClientMappingRemoveCommand) {
        LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkServerPermeateClientMappingRemoveCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkServerPermeateClientMapping.setServerId(serverId);
        Result<LazyInternalNetworkServerPermeateClientMapping> remove = lazyInternalNetworkServerPermeateClientMappingRepository.remove(lazyInternalNetworkServerPermeateClientMapping);
        // 删除端口映射
        String clientId = lazyInternalNetworkServerPermeateClientMappingRemoveCommand.getClientId();
        Integer visitorPort = lazyInternalNetworkServerPermeateClientMappingRemoveCommand.getVisitorPort();
        this.changeCloseSocket(clientId, visitorPort);
        return remove;
    }

    /**
     * 创建客户端的访问者
     *
     * @param clientId 客户端ID
     */
    @Override
    public Result<Void> createVisitor(String clientId) {
        // 查询代理信息
        LazyInternalNetworkServerPermeateClientMapping lazyInternalNetworkServerPermeateClientMapping = new LazyInternalNetworkServerPermeateClientMapping();
        lazyInternalNetworkServerPermeateClientMapping.setIsDeleted(false);
        lazyInternalNetworkServerPermeateClientMapping.setClientId(clientId);
        return lazyInternalNetworkServerPermeateClientMappingRepository
                .findList(lazyInternalNetworkServerPermeateClientMapping)
                .applyOther(internalNetworkPenetrationMappings -> {
                    for (LazyInternalNetworkServerPermeateClientMapping networkPenetrationMapping : internalNetworkPenetrationMappings) {
                        Integer visitorPort = networkPenetrationMapping.getVisitorPort();
                        String clientTargetIp = networkPenetrationMapping.getClientTargetIp();
                        Integer clientTargetPort = networkPenetrationMapping.getClientTargetPort();
                        this.createVisitor(clientId, clientTargetIp, clientTargetPort, visitorPort);
                    }
                    return ResultFactory.successOf();
                });
    }

    /**
     * 创建访客
     *
     * @param clientId         客户端ID
     * @param clientTargetIp   客户端目标IP
     * @param clientTargetPort 客户端目标端口
     * @param visitorPort      访客端口
     */
    private void createVisitor(String clientId, String clientTargetIp, Integer clientTargetPort, Integer visitorPort) {
        // 更新 客户端映射
        NettyServerPermeateClientVisitorSocket nettyServerPermeateClientVisitorSocket = NettyServerPermeateClientVisitorSocket.NettyVisitorSocketBuilder
                .builder()
                .builderClientId(clientId)
                .builderClientTargetIp(clientTargetIp)
                .builderClientTargetPort(clientTargetPort)
                .builderVisitorPort(visitorPort)
                .builderChannelFlowAdapter(channelFlowAdapter)
                .build();
        try {
            nettyServerPermeateClientVisitorSocket.start();
        } catch (Exception e) {
            log.error("客户端:{},网络端口:{},开放失败", clientId, visitorPort);
            throw new RuntimeException(e);
        }
    }
}