package com.sino.mci.channel.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sino.mci.channel.common.model.ChannelType;
import com.sino.mci.channel.entity.ChannelAccount;
import com.sino.mci.channel.entity.ChannelAccountConversation;
import com.sino.mci.channel.repository.ChannelAccountConversationMapper;
import com.sino.mci.channel.repository.ChannelAccountMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class AccountSelector {

    private static final int BINDING_SEND_PRIMARY = 1;
    private static final int BINDING_SEND_BACKUP = 2;

    private static final int STATUS_ENABLED = 1;
    private static final int ONLINE_STATUS_ONLINE = 1;
    private static final int RISK_LEVEL_HIGH = 3;
    private static final int ACCOUNT_TYPE_PYWECHAT = 3;
    private static final int HEARTBEAT_TIMEOUT_MINUTES = 5;

    private final ChannelAccountConversationMapper accountConversationMapper;
    private final ChannelAccountMapper accountMapper;

    public Long selectSendAccount(String tenantCode, Long conversationId, ChannelType channelType, String preferredStrategy) {
        List<Long> accounts = selectSendAccounts(tenantCode, conversationId, channelType, preferredStrategy);
        return CollectionUtils.isEmpty(accounts) ? null : accounts.get(0);
    }

    public List<Long> selectSendAccounts(String tenantCode, Long conversationId, ChannelType channelType, String preferredStrategy) {
        if (conversationId == null || channelType == null) {
            return Collections.emptyList();
        }

        List<ChannelAccountConversation> bindings = accountConversationMapper.selectList(
                new LambdaQueryWrapper<ChannelAccountConversation>()
                        .eq(ChannelAccountConversation::getConversationId, conversationId)
                        .in(ChannelAccountConversation::getBindingType, BINDING_SEND_PRIMARY, BINDING_SEND_BACKUP)
                        .eq(ChannelAccountConversation::getStatus, STATUS_ENABLED)
                        .orderByAsc(ChannelAccountConversation::getBindingType)
                        .orderByAsc(ChannelAccountConversation::getSortOrder));

        if (CollectionUtils.isEmpty(bindings)) {
            return Collections.emptyList();
        }

        if ("backup".equals(preferredStrategy)) {
            bindings.sort(Comparator.comparingInt(ChannelAccountConversation::getBindingType).reversed()
                    .thenComparingInt(b -> b.getSortOrder() == null ? 0 : b.getSortOrder()));
        }

        List<Long> healthyAccountIds = new ArrayList<>();
        for (ChannelAccountConversation binding : bindings) {
            ChannelAccount account = accountMapper.selectById(binding.getAccountId());
            if (isHealthy(account, tenantCode, channelType)) {
                healthyAccountIds.add(account.getId());
            }
        }
        return healthyAccountIds;
    }

    public static boolean isHealthy(ChannelAccount account, String tenantCode, ChannelType channelType) {
        if (account == null) {
            return false;
        }
        if (tenantCode != null && !tenantCode.equals(account.getTenantCode())) {
            return false;
        }
        if (account.getStatus() == null || account.getStatus() != STATUS_ENABLED) {
            return false;
        }
        if (!channelType.name().equalsIgnoreCase(account.getChannelType())) {
            return false;
        }
        if (account.getRiskLevel() != null && account.getRiskLevel() >= RISK_LEVEL_HIGH) {
            return false;
        }
        if (account.getAccountType() != null && account.getAccountType() == ACCOUNT_TYPE_PYWECHAT) {
            if (account.getOnlineStatus() == null || account.getOnlineStatus() != ONLINE_STATUS_ONLINE) {
                return false;
            }
            LocalDateTime lastHeartbeat = account.getLastHeartbeat();
            if (lastHeartbeat == null
                    || lastHeartbeat.isBefore(LocalDateTime.now().minusMinutes(HEARTBEAT_TIMEOUT_MINUTES))) {
                return false;
            }
        }
        return true;
    }
}
