package io.wdd.server.coreService.impl;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.wdd.server.beans.po.*;
import io.wdd.server.beans.request.ServerQueryEntity;
import io.wdd.server.beans.vo.AppInfoVO;
import io.wdd.server.beans.vo.DomainInfoVO;
import io.wdd.server.beans.vo.ServerInfoVO;
import io.wdd.server.coreService.CoreServerService;
import io.wdd.server.service.*;
import io.wdd.server.utils.EntityConvertUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.springframework.beans.BeanUtils.copyProperties;


@Service
@Slf4j
public class CoreServerServiceImpl implements CoreServerService {

    @Resource
    ServerInfoService serverInfoService;

    @Resource
    ServerAppRelationService serverAppRelationService;

    @Resource
    AppInfoService appInfoService;

    @Resource
    ServerDomainRelationService serverDomainRelationService;

    @Resource
    DomainInfoService domainInfoService;

    @Override
    public List<ServerInfoVO> serverGetSingle(String serverName, String ipv4, String serverLocation) {

        // ignore if deleted !
        List<ServerInfoPO> list = new LambdaQueryChainWrapper<>(serverInfoService.getBaseMapper())
                .eq(
                        StringUtils.isNoneEmpty(serverName),
                        ServerInfoPO::getServerName,
                        serverName
                )
                .eq(
                        StringUtils.isNoneEmpty(ipv4),
                        ServerInfoPO::getServerIpPbV4,
                        ipv4
                )
                .eq(
                        StringUtils.isNoneEmpty(serverLocation),
                        ServerInfoPO::getLocation,
                        serverLocation
                )
                .list();
        return this.covertServerPOtoVO(
                list,
                0
        );

    }

    @Override
    public List<ServerInfoPO> serverGetAll() {

        List<ServerInfoPO> serverInfoPOWithOutDelete = serverInfoService.list();


        return serverInfoPOWithOutDelete;
    }

    @Override
    public Page<ServerInfoPO> serverGetByPage(ServerQueryEntity serverQueryEntity) {

        // 默认数值处理
        if (serverQueryEntity == null) {
            serverQueryEntity = ServerQueryEntity
                    .builder()
                    .pageNumber(1)
                    .pageSize(10)
                    .build();
        }

        if (serverQueryEntity.getPageSize() == null || serverQueryEntity.getPageNumber() == null) {
            serverQueryEntity.setPageSize(10);
            serverQueryEntity.setPageNumber(1);
        }

        Page<ServerInfoPO> serverInfoPOPage = new Page<>(
                serverQueryEntity.getPageNumber(),
                serverQueryEntity.getPageSize()
        );

        // 查询Page，会直接写入到serverInfoPOPage中
        new LambdaQueryChainWrapper<ServerInfoPO>(serverInfoService.getBaseMapper())
                .likeRight(
                        StringUtils.isNotEmpty(serverQueryEntity.getServerName()),
                        ServerInfoPO::getServerName,
                        serverQueryEntity.getServerName()
                )
                .likeRight(
                        StringUtils.isNotEmpty(serverQueryEntity.getServerIPv4()),
                        ServerInfoPO::getServerIpPbV4,
                        serverQueryEntity.getServerIPv4()
                )
                .likeRight(
                        StringUtils.isNotEmpty(serverQueryEntity.getServerLocation()),
                        ServerInfoPO::getLocation,
                        serverQueryEntity.getServerLocation()
                )
                .page(serverInfoPOPage);

        // 将查询结果中的User对象转换为UserVO对象
        /*Page<ServerInfoVO> serverInfoVOPage = new Page<>(
                serverInfoPOPage.getCurrent(),
                serverInfoPOPage.getSize()
        );
        serverInfoVOPage.setTotal(serverInfoPOPage.getTotal());
        serverInfoVOPage.setRecords(
                this.covertServerPOtoVO(
                        serverInfoPOPage.getRecords(),
                        (serverQueryEntity.getPageNumber() - 1) * serverQueryEntity.getPageSize()
                )
        );*/

        return serverInfoPOPage;
    }

    @Override
    public List<ServerInfoVO> serverGetAllIncludeDelete() {

        // todo how to  solve the problem?
//        this.covertServerPOtoVO(serverInfoService.getAll());

        return null;
    }

    @Override
    public boolean serverCreate(ServerInfoVO serverInfoVO) {

        if (serverInfoVO == null) {
            return false;
        }

        ServerInfoPO serverInfoPO = EntityConvertUtils.cvToTarget(
                serverInfoVO,
                ServerInfoPO.class
        );

        // 唯一性约束判定，判定ServerName就可以覆盖topicName
        Optional<ServerInfoPO> serverNameOp = new LambdaQueryChainWrapper<ServerInfoPO>(serverInfoService.getBaseMapper()).
                eq(
                        ServerInfoPO::getServerName,
                        serverInfoVO.getServerName()
                )
                .oneOpt();
        if (!serverNameOp.isEmpty()) {
            return false;
        }


        return serverInfoService.save(serverInfoPO);
    }

    @Override
    public boolean serverCreateOrUpdate(ServerInfoVO serverInfoVO) {

        ServerInfoPO po = new LambdaQueryChainWrapper<ServerInfoPO>(serverInfoService.getBaseMapper())
                .eq(
                        ServerInfoPO::getServerName,
                        serverInfoVO.getServerName()
                )
                .one();

        if (po != null) {
            // 已经存在，需要把更新的信息 复制到旧的内容之上
            copyProperties(
                    serverInfoVO,
                    po
            );
        } else {
            // 没有
            // 创建新的PO
            po = EntityConvertUtils.cvToTarget(
                    serverInfoVO,
                    ServerInfoPO.class
            );
        }

        return serverInfoService.saveOrUpdate(po);
    }

    @Override
    public boolean serverUpdate(ServerInfoPO serverInfoPO) {

        if (serverInfoPO == null || serverInfoPO.getServerId() == null) {
            return false;
        }

        return new LambdaUpdateChainWrapper<>(serverInfoService.getBaseMapper())
                .eq(
                        ServerInfoPO::getServerId,
                        serverInfoPO.getServerId()
                )
                .update(serverInfoPO);
    }

    @Override
    public boolean serverDelete(Long serverId, String serverName) {

        if (serverId == null && StringUtils.isBlank(serverName)) {
            return false;
        }

        // set isDelete = 1
        return new LambdaUpdateChainWrapper<>(serverInfoService.getBaseMapper())
                .eq(
                        serverId != null,
                        ServerInfoPO::getServerId,
                        serverId
                )
                .eq(
                        StringUtils.isNoneEmpty(serverName),
                        ServerInfoPO::getServerName,
                        serverName
                )
                .set(
                        ServerInfoPO::getIsDelete,
                        1
                )
                .update();

    }

    @Override
    public List<AppInfoVO> appGetAll(Long serverId) {

        // serverInfo --- server_app_relation --- appInfo

        List<ServerAppRelationPO> serverAppRelationPOList = new LambdaQueryChainWrapper<ServerAppRelationPO>(serverAppRelationService.getBaseMapper())
                .eq(
                        ServerAppRelationPO::getServerId,
                        serverId
                )
                .list();

        Assert.notEmpty(
                serverAppRelationPOList,
                "No server find"
        );

        // query the app info with specific server id
        List<AppInfoPO> appInfoPOList = appInfoService.listByIds(serverAppRelationPOList
                                                                         .stream()
                                                                         .map(
                                                                                 serverAppRelationPO -> serverAppRelationPO.getAppId()
                                                                         )
                                                                         .collect(Collectors.toList()));


        return EntityConvertUtils.cvToTarget(
                appInfoPOList,
                AppInfoVO.class
        );
    }

    @Override
    @Transactional
    public AppInfoVO appCreate(Long serverId, AppInfoVO appInfoVO) {

        Assert.notNull(
                serverInfoService.getById(serverId),
                "server not find, can't create a app"
        );

        // 1- save appInfo itself
        AppInfoPO appInfoPO = EntityConvertUtils.cvToTarget(
                appInfoVO,
                AppInfoPO.class
        );
        appInfoService.save(appInfoPO);

        // 2. create the relation
        ServerAppRelationPO relationPO = new ServerAppRelationPO();
        relationPO.setServerId(serverId);
        relationPO.setAppId(appInfoPO.getAppId());
        serverAppRelationService.save(relationPO);


        return EntityConvertUtils.cvToTarget(
                appInfoPO,
                AppInfoVO.class
        );
    }

    @Override
    @Transactional
    public boolean appDelete(Long serverId, Long appId) {

        Assert.notNull(
                serverInfoService.getById(serverId),
                "server not find, can't delete a app"
        );
        Assert.notNull(
                appInfoService.getById(appId),
                "app not find, can't delete a app"
        );

        // 1. delete the relation
        serverAppRelationService.removeById(serverId);
        // 2. delete the app
        appInfoService.removeById(appId);

        return true;
    }

    private List<ServerInfoVO> covertServerPOtoVO(List<ServerInfoPO> serverInfoPOList, int startIndex) {

        if (null == serverInfoPOList || serverInfoPOList.size() == 0) {
            return Collections.emptyList();
        }
        ArrayList<ServerInfoVO> serverInfoVOList = new ArrayList<>(32);


        for (int i = 0; i < serverInfoPOList.size(); i++) {

            ServerInfoVO serverInfoVO = new ServerInfoVO();

            copyProperties(
                    serverInfoPOList.get(i),
                    serverInfoVO
            );

            // 2023年7月13日 设置序号
            serverInfoVO.setId(startIndex++);

            serverInfoVOList.add(serverInfoVO);
        }

        return serverInfoVOList;

    }

    /*
     * below is server associated domain
     *  server --- domain
     *  1 ----------- n
     * */

    @Override
    public List<DomainInfoVO> domainGetAll(Long serverId) {
        Assert.notNull(
                serverInfoService.getById(serverId),
                "server not find, can't create a app"
        );

        List<ServerDomainRelationPO> domainRelationPOList = new LambdaQueryChainWrapper<ServerDomainRelationPO>(serverDomainRelationService.getBaseMapper())
                .eq(
                        ServerDomainRelationPO::getServerId,
                        serverId
                )
                .list();


        List<DomainInfoPO> domainInfoPOList = domainInfoService.listByIds(domainRelationPOList
                                                                                  .stream()
                                                                                  .map(
                                                                                          domainRelationPO -> domainRelationPO.getDomainId()
                                                                                  )
                                                                                  .collect(Collectors.toList()));


        return EntityConvertUtils.cvToTarget(
                domainInfoPOList,
                DomainInfoVO.class
        );
    }

    @Override
    public List<DomainInfoVO> domainGetSingle(Long serverId, String domainName, String dnsIP) {

        Assert.notNull(
                serverInfoService.getById(serverId),
                "server not find, can't create a app"
        );

        List<ServerDomainRelationPO> domainRelationPOList = new LambdaQueryChainWrapper<ServerDomainRelationPO>(serverDomainRelationService.getBaseMapper())
                .eq(
                        ServerDomainRelationPO::getServerId,
                        serverId
                )
                .list();


        List<DomainInfoPO> domainInfoPOList = domainRelationPOList
                .stream()
                .map(
                        domainPO -> {
                            // query single according to every server id related domain ID
                            return new LambdaQueryChainWrapper<DomainInfoPO>(domainInfoService.getBaseMapper())
                                    .eq(
                                            DomainInfoPO::getDomainId,
                                            domainPO.getDomainId()
                                    )
                                    .like(
                                            StringUtils.isNotEmpty(domainName),
                                            DomainInfoPO::getDomainName,
                                            domainName
                                    )
                                    .eq(
                                            StringUtils.isNoneEmpty(dnsIP),
                                            DomainInfoPO::getDnsIp,
                                            dnsIP
                                    )
                                    .one();
                        }
                )
                .collect(Collectors.toList());


        return EntityConvertUtils.cvToTarget(
                domainInfoPOList,
                DomainInfoVO.class
        );
    }

    @Override
    public boolean domainCreate(Long serverId, DomainInfoVO domainInfoVO) {
        return false;
    }

    @Override
    public boolean domainUpdate(DomainInfoPO domainInfoPO) {
        return false;
    }

    @Override
    public boolean domainDelete(Long serverId, Long domainId) {
        return false;
    }


}
