package com.bocloud.ims.repository.resource.hmc;

import com.bocloud.cmp.model.hmc.enums.LogicalPartitionType;
import com.bocloud.ims.entity.common.MapBuilder;
import com.bocloud.ims.entity.resource.hmc.HmdPartition;
import com.bocloud.ims.entity.resource.hmc.HmdServer;
import com.bocloud.ims.entity.resource.hmc.HmdServerCapability;
import com.bocloud.ims.entity.resource.hmc.HmdServerHea;
import com.bocloud.ims.entity.resource.hmc.HmdServerIoAdapter;
import com.bocloud.ims.entity.resource.hmc.HmdServerSharedProcessorPool;
import com.bocloud.ims.entity.resource.hmc.HmdServerVirtualBridge;
import com.bocloud.ims.entity.resource.hmc.HmdServerVirtualNetwork;
import com.bocloud.ims.entity.resource.hmc.HmdServerVirtualSwitch;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Repository
public abstract class HmdServerRepository extends HmdGenericDao<HmdServer> {

    @Lookup protected abstract HmdPartitionRepository partitionRepository();
    @Lookup protected abstract HmdServerCapabilityRepository serverCapabilityRepository();
    @Lookup protected abstract HmdServerHeaRepository serverHeaRepository();
    @Lookup protected abstract HmdServerIoAdapterRepository serverIoAdapterRepository();
    @Lookup protected abstract HmdServerSharedProcessorPoolRepository serverSharedProcessorPoolRepository();
    @Lookup protected abstract HmdServerVirtualBridgeRepository serverVirtualBridgeRepository();
    @Lookup protected abstract HmdServerVirtualNetworkRepository serverVirtualNetworkRepository();
    @Lookup protected abstract HmdServerVirtualSwitchRepository serverVirtualSwitchRepository();

    public HmdServerRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate npJdbcTemplate) {
        super(jdbcTemplate, npJdbcTemplate);
    }

    @Override
    public void extra(HmdServer server) {
        Optional.of(server).map(HmdServer::getId).map(this.serverIoAdapterRepository()::listByServerId).ifPresent(server::setIoAdapters);
        Optional.of(server).map(HmdServer::getId).map(this.serverSharedProcessorPoolRepository()::listByServerId).ifPresent(server::setSharedProcessorPools);
        Optional.of(server).map(HmdServer::getId).map(this.serverCapabilityRepository()::listByServerId).ifPresent(server::setCapabilities);
        Optional.of(server).map(HmdServer::getId).map(this.serverVirtualBridgeRepository()::listByServerId).ifPresent(server::setVirtualBridges);
        Optional.of(server).map(HmdServer::getId).map(this.serverVirtualNetworkRepository()::listByServerId).ifPresent(server::setVirtualNetworks);
        Optional.of(server).map(HmdServer::getId).map(this.serverVirtualSwitchRepository()::listByServerId).ifPresent(server::setVirtualSwitches);
        Optional.of(server).map(HmdServer::getId).map(this.serverHeaRepository()::listByServerId).ifPresent(server::setHeas);
        Optional.of(server).map(HmdServer::getId).map(serverId ->
            this.partitionRepository().listByServerId(serverId).stream().filter(partition ->
                partition.getType().equals(LogicalPartitionType.AIX_LINUX.getType()) ||
                    partition.getType().equals(LogicalPartitionType.IBMI.getType())
            ).collect(Collectors.toList())
        ).ifPresent(server::setLpars);
        Optional.of(server).map(HmdServer::getId).map(serverId ->
            this.partitionRepository().listByServerId(serverId).stream().filter(partition ->
                partition.getType().equals(LogicalPartitionType.VIOS.getType())
            ).collect(Collectors.toList())
        ).ifPresent(server::setVioses);
    }

    public List<HmdServer> listByVendorId(long parentId) {
        return this.search(Collections.singletonMap("a.vendor_id", parentId));
    }

    public List<HmdServer> search(long vendorId, String ip) {
        Map<String, Object> map = MapBuilder.init()
            .put("a.vendor_id", vendorId)
            .put("a.primary_ip_address", ip)
            .get();
        return this.search(map);
    }

    public HmdServer getById(long id) {
        return this.search(Collections.singletonMap("a.id", id)).stream().findFirst().orElse(null);
    }

    public HmdServer getByUuid(String uuid) {
        return this.search(Collections.singletonMap("a.uuid", uuid)).stream().findFirst().orElse(null);
    }

    @Override
    public void clear(long id) {
        this.partitionRepository().listByServerId(id).stream().map(HmdPartition::getId).forEach(this.partitionRepository()::clear);

        this.serverCapabilityRepository().listByServerId(id).stream().map(HmdServerCapability::getId).forEach(this.serverCapabilityRepository()::clear);
        this.serverHeaRepository().listByServerId(id).stream().map(HmdServerHea::getId).forEach(this.serverHeaRepository()::clear);
        this.serverIoAdapterRepository().listByServerId(id).stream().map(HmdServerIoAdapter::getId).forEach(this.serverIoAdapterRepository()::clear);
        this.serverSharedProcessorPoolRepository().listByServerId(id).stream().map(HmdServerSharedProcessorPool::getId).forEach(this.serverSharedProcessorPoolRepository()::clear);
        this.serverVirtualBridgeRepository().listByServerId(id).stream().map(HmdServerVirtualBridge::getId).forEach(this.serverVirtualBridgeRepository()::clear);
        this.serverVirtualNetworkRepository().listByServerId(id).stream().map(HmdServerVirtualNetwork::getId).forEach(this.serverVirtualNetworkRepository()::clear);
        this.serverVirtualSwitchRepository().listByServerId(id).stream().map(HmdServerVirtualSwitch::getId).forEach(this.serverVirtualSwitchRepository()::clear);

        this.delete(id);
    }

    @Override
    public void startAllPending(long id) {
        this.partitionRepository().listByServerId(id).stream().map(HmdPartition::getId).forEach(this.partitionRepository()::startAllPending);

        this.serverCapabilityRepository().listByServerId(id).stream().map(HmdServerCapability::getId).forEach(this.serverCapabilityRepository()::startAllPending);
        this.serverHeaRepository().listByServerId(id).stream().map(HmdServerHea::getId).forEach(this.serverHeaRepository()::startAllPending);
        this.serverIoAdapterRepository().listByServerId(id).stream().map(HmdServerIoAdapter::getId).forEach(this.serverIoAdapterRepository()::startAllPending);
        this.serverSharedProcessorPoolRepository().listByServerId(id).stream().map(HmdServerSharedProcessorPool::getId).forEach(this.serverSharedProcessorPoolRepository()::startAllPending);
        this.serverVirtualBridgeRepository().listByServerId(id).stream().map(HmdServerVirtualBridge::getId).forEach(this.serverVirtualBridgeRepository()::startAllPending);
        this.serverVirtualNetworkRepository().listByServerId(id).stream().map(HmdServerVirtualNetwork::getId).forEach(this.serverVirtualNetworkRepository()::startAllPending);
        this.serverVirtualSwitchRepository().listByServerId(id).stream().map(HmdServerVirtualSwitch::getId).forEach(this.serverVirtualSwitchRepository()::startAllPending);

        this.startPending(id);
    }

    @Override
    public void stopAllPending(long id) {
        this.partitionRepository().listByServerId(id).stream().map(HmdPartition::getId).forEach(this.partitionRepository()::stopAllPending);

        this.serverCapabilityRepository().listByServerId(id).stream().map(HmdServerCapability::getId).forEach(this.serverCapabilityRepository()::stopAllPending);
        this.serverHeaRepository().listByServerId(id).stream().map(HmdServerHea::getId).forEach(this.serverHeaRepository()::stopAllPending);
        this.serverIoAdapterRepository().listByServerId(id).stream().map(HmdServerIoAdapter::getId).forEach(this.serverIoAdapterRepository()::stopAllPending);
        this.serverSharedProcessorPoolRepository().listByServerId(id).stream().map(HmdServerSharedProcessorPool::getId).forEach(this.serverSharedProcessorPoolRepository()::stopAllPending);
        this.serverVirtualBridgeRepository().listByServerId(id).stream().map(HmdServerVirtualBridge::getId).forEach(this.serverVirtualBridgeRepository()::stopAllPending);
        this.serverVirtualNetworkRepository().listByServerId(id).stream().map(HmdServerVirtualNetwork::getId).forEach(this.serverVirtualNetworkRepository()::stopAllPending);
        this.serverVirtualSwitchRepository().listByServerId(id).stream().map(HmdServerVirtualSwitch::getId).forEach(this.serverVirtualSwitchRepository()::stopAllPending);

        this.startPending(id);
    }

    @Override
    public void syncInstance(HmdServer newT, HmdServer oldT, long userId) {
        super.syncInstance(newT, oldT, userId);
        Optional.of(newT).map(HmdServer::getIoAdapters).ifPresent(newAdapters -> {
            newAdapters.forEach(adapter -> {
                adapter.setServerId(newT.getId());
                adapter.setServer(newT);
            });
            List<HmdServerIoAdapter> oldAdapters = this.serverIoAdapterRepository().listByServerId(newT.getId());
            this.serverIoAdapterRepository().syncList(newAdapters, oldAdapters, userId);
        });
        Optional.of(newT).map(HmdServer::getSharedProcessorPools).ifPresent(newPools -> {
            newPools.forEach(pool -> {
                pool.setServerId(newT.getId());
                pool.setServer(newT);
            });
            List<HmdServerSharedProcessorPool> oldAdapters = this.serverSharedProcessorPoolRepository().listByServerId(newT.getId());
            this.serverSharedProcessorPoolRepository().syncList(newPools, oldAdapters, userId);
        });
        Optional.of(newT).map(HmdServer::getCapabilities).ifPresent(newCapabilities -> {
            newCapabilities.forEach(capability -> {
                capability.setServerId(newT.getId());
                capability.setServer(newT);
            });
            List<HmdServerCapability> oldCapabilities = this.serverCapabilityRepository().listByServerId(newT.getId());
            this.serverCapabilityRepository().syncList(newCapabilities, oldCapabilities, userId);
        });
        Optional.of(newT).map(HmdServer::getVirtualBridges).ifPresent(newBridges -> {
            newBridges.forEach(bridge -> {
                bridge.setServerId(newT.getId());
                bridge.setServer(newT);
            });
            List<HmdServerVirtualBridge> oldBridges = this.serverVirtualBridgeRepository().listByServerId(newT.getId());
            this.serverVirtualBridgeRepository().syncList(newBridges, oldBridges, userId);
        });
        Optional.of(newT).map(HmdServer::getVirtualNetworks).ifPresent(newNetworks -> {
            newNetworks.forEach(network -> {
                network.setServerId(newT.getId());
                network.setServer(newT);
            });
            List<HmdServerVirtualNetwork> oldNetworks = this.serverVirtualNetworkRepository().listByServerId(newT.getId());
            this.serverVirtualNetworkRepository().syncList(newNetworks, oldNetworks, userId);
        });
        Optional.of(newT).map(HmdServer::getVirtualSwitches).ifPresent(newSwitches -> {
            newSwitches.forEach(vSwitch -> {
                vSwitch.setServerId(newT.getId());
                vSwitch.setServer(newT);
            });
            List<HmdServerVirtualSwitch> oldSwitches = this.serverVirtualSwitchRepository().listByServerId(newT.getId());
            this.serverVirtualSwitchRepository().syncList(newSwitches, oldSwitches, userId);
        });
        Optional.of(newT).map(HmdServer::getHeas).ifPresent(newHeas -> {
            newHeas.forEach(hea -> {
                hea.setServerId(newT.getId());
                hea.setServer(newT);
            });
            List<HmdServerHea> oldHeas = this.serverHeaRepository().listByServerId(newT.getId());
            this.serverHeaRepository().syncList(newHeas, oldHeas, userId);
        });
        Optional.of(newT).map(HmdServer::getLpars).ifPresent(newLpars -> {
            newLpars.forEach(lpar -> {
                lpar.setServerId(newT.getId());
                lpar.setServer(newT);
            });
            List<HmdPartition> oldLpars = this.partitionRepository().listByServerId(newT.getId())
                .stream().filter(partition ->
                    partition.getType().equals(LogicalPartitionType.AIX_LINUX.getType()) ||
                        partition.getType().equals(LogicalPartitionType.IBMI.getType())
                ).collect(Collectors.toList());
            this.partitionRepository().syncList(newLpars, oldLpars, userId);
        });
        Optional.of(newT).map(HmdServer::getVioses).ifPresent(newVioses -> {
            newVioses.forEach(vios -> {
                vios.setServerId(newT.getId());
                vios.setServer(newT);
            });
            List<HmdPartition> oldVioses = this.partitionRepository().listByServerId(newT.getId())
                .stream().filter(partition -> partition.getType().equals(LogicalPartitionType.VIOS.getType()))
                .collect(Collectors.toList());
            this.partitionRepository().syncList(newVioses, oldVioses, userId);
        });
    }
}
