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

import com.bocloud.cmp.model.hmc.enums.VirtualAdapterTargetType;
import com.bocloud.ims.entity.common.MapBuilder;
import com.bocloud.ims.entity.resource.hmc.HmdPartition;
import com.bocloud.ims.entity.resource.hmc.HmdProfile;
import com.bocloud.ims.entity.resource.hmc.HmdServer;
import com.bocloud.ims.entity.resource.hmc.HmdVirtualFabricAdapter;
import com.bocloud.ims.entity.resource.hmc.HmdVirtualFabricAdapterWwpn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.concurrent.atomic.AtomicReference;

@Repository
public abstract class HmdVirtualFabricAdapterRepository extends HmdGenericDao<HmdVirtualFabricAdapter> {

    private static final Logger logger = LoggerFactory.getLogger(HmdVirtualFabricAdapterRepository.class);

    @Lookup protected abstract HmdServerRepository serverRepository();
    @Lookup protected abstract HmdPartitionRepository partitionRepository();
    @Lookup protected abstract HmdProfileRepository profileRepository();
    @Lookup protected abstract HmdVirtualFabricAdapterWwpnRepository virtualFabricAdapterWwpnRepository();

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

    @Override
    public void extra(HmdVirtualFabricAdapter adapter) {
        Optional.of(adapter).map(HmdVirtualFabricAdapter::getTargetType).map(VirtualAdapterTargetType::valueOf).ifPresent(targetType -> {
            switch (targetType) {
                case PARTITION: {
                    Optional.of(adapter).map(HmdVirtualFabricAdapter::getTargetId).map(this.partitionRepository()::getById).ifPresent(adapter::setPartition);
                    break;
                }
                case PROFILE: {
                    Optional.of(adapter).map(HmdVirtualFabricAdapter::getTargetId).map(this.profileRepository()::getById).ifPresent(adapter::setProfile);
                    break;
                }
                default:
                    throw new IllegalArgumentException("未知的类型：" + targetType.name());
            }
        });
        Optional.of(adapter).map(HmdVirtualFabricAdapter::getRemotePartitionId).map(this.partitionRepository()::getById).ifPresent(adapter::setRemotePartition);
    }

    public List<HmdVirtualFabricAdapter> listByPartitionId(long partitionId) {
        Map<String, Object> map = MapBuilder.init()
            .put("a.target_id", partitionId)
            .put("a.type", VirtualAdapterTargetType.PARTITION.name())
            .get();
        return this.search(map);
    }

    public List<HmdVirtualFabricAdapter> listByProfileId(long profileId) {
        Map<String, Object> map = MapBuilder.init()
            .put("a.target_id", profileId)
            .put("a.type", VirtualAdapterTargetType.PROFILE.name())
            .get();
        return this.search(map);
    }

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

    @Override
    public void clear(long id) {
        this.virtualFabricAdapterWwpnRepository().listByAdapterId(id).stream().map(HmdVirtualFabricAdapterWwpn::getId).forEach(this.virtualFabricAdapterWwpnRepository()::clear);
        this.delete(id);
    }

    @Override
    public void convertInstance(final HmdVirtualFabricAdapter srcT, final HmdVirtualFabricAdapter desT) {
        AtomicReference<HmdServer> server = new AtomicReference<>();
        Optional.of(srcT).map(HmdVirtualFabricAdapter::getTargetType).map(VirtualAdapterTargetType::valueOf).ifPresent(targetType -> {
            switch (targetType) {
                case PARTITION: {
                    server.set(Optional.of(srcT)
                        .map(HmdVirtualFabricAdapter::getPartition)
                        .map(HmdPartition::getServerId)
                        .map(this.serverRepository()::getById)
                        .orElse(null)
                    );
                    break;
                }
                case PROFILE: {
                    server.set(Optional.of(srcT)
                        .map(HmdVirtualFabricAdapter::getProfile)
                        .map(HmdProfile::getPartitionId)
                        .map(this.partitionRepository()::getById)
                        .map(HmdPartition::getServerId)
                        .map(this.serverRepository()::getById)
                        .orElse(null)
                    );
                    break;
                }
                default:
                    throw new IllegalArgumentException("未知的类型：" + targetType.name());
            }
        });

        Optional<HmdPartition> partition = Optional.of(srcT).map(HmdVirtualFabricAdapter::getRemotePartition).map(HmdPartition::getUuid).map(this.partitionRepository()::getByUuid);
        partition.map(HmdPartition::getId).ifPresent(srcT::setRemotePartitionId);
        partition.ifPresent(srcT::setRemotePartition);

        if (!partition.isPresent() && server.get() != null) {
            partition = Optional.of(srcT).map(HmdVirtualFabricAdapter::getRemotePartition)
                .map(HmdPartition::getPartitionId)
                .map(partitonId -> this.partitionRepository().getByPartitionId(server.get().getId(), partitonId));
            partition.map(HmdPartition::getId).ifPresent(srcT::setRemotePartitionId);
            partition.ifPresent(srcT::setRemotePartition);
        }

        this.copy(srcT, desT);
    }

    @Override
    public void syncInstance(HmdVirtualFabricAdapter newT, HmdVirtualFabricAdapter oldT, long userId) {
        super.syncInstance(newT, oldT, userId);

        Optional.of(newT).map(HmdVirtualFabricAdapter::getWwpns).ifPresent(newWwpn -> {
            newWwpn.forEach(wwpn -> {
                wwpn.setAdapterId(newT.getId());
                wwpn.setFabricAdapter(newT);
            });
            List<HmdVirtualFabricAdapterWwpn> oldWwpns = this.virtualFabricAdapterWwpnRepository().listByAdapterId(newT.getId());
            this.virtualFabricAdapterWwpnRepository().syncList(newT.getWwpns(), oldWwpns, userId);
        });
    }
}
