package com.sino.mci.channel.service;

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 org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class AccountSelectorTest {

    @Mock
    private ChannelAccountConversationMapper accountConversationMapper;

    @Mock
    private ChannelAccountMapper accountMapper;

    private AccountSelector accountSelector;

    @BeforeEach
    void setUp() {
        accountSelector = new AccountSelector(accountConversationMapper, accountMapper);
    }

    @Test
    void primarySelectedWhenHealthy() {
        ChannelAccountConversation primary = createBinding(1L, 1, 1);
        when(accountConversationMapper.selectList(any())).thenReturn(Collections.singletonList(primary));
        when(accountMapper.selectById(1L)).thenReturn(createHealthyAccount(1L));

        Long accountId = accountSelector.selectSendAccount("tenant", 100L, ChannelType.WECHAT_PERSONAL, "primary");

        assertEquals(1L, accountId);
    }

    @Test
    void backupSelectedWhenPrimaryUnhealthy() {
        ChannelAccountConversation primary = createBinding(1L, 1, 1);
        ChannelAccountConversation backup = createBinding(2L, 2, 1);
        when(accountConversationMapper.selectList(any())).thenReturn(Arrays.asList(primary, backup));
        when(accountMapper.selectById(1L)).thenReturn(createOfflineAccount(1L));
        when(accountMapper.selectById(2L)).thenReturn(createHealthyAccount(2L));

        Long accountId = accountSelector.selectSendAccount("tenant", 100L, ChannelType.WECHAT_PERSONAL, "primary");

        assertEquals(2L, accountId);
    }

    @Test
    void returnsNullWhenNoHealthyAccount() {
        ChannelAccountConversation primary = createBinding(1L, 1, 1);
        ChannelAccountConversation backup = createBinding(2L, 2, 1);
        when(accountConversationMapper.selectList(any())).thenReturn(Arrays.asList(primary, backup));
        when(accountMapper.selectById(1L)).thenReturn(createOfflineAccount(1L));
        when(accountMapper.selectById(2L)).thenReturn(createHighRiskAccount(2L));

        Long accountId = accountSelector.selectSendAccount("tenant", 100L, ChannelType.WECHAT_PERSONAL, "primary");

        assertNull(accountId);
    }

    @Test
    void preferredBackupStrategySelectsBackupFirst() {
        ChannelAccountConversation primary = createBinding(1L, 1, 1);
        ChannelAccountConversation backup = createBinding(2L, 2, 1);
        when(accountConversationMapper.selectList(any())).thenReturn(Arrays.asList(primary, backup));
        when(accountMapper.selectById(1L)).thenReturn(createHealthyAccount(1L));
        when(accountMapper.selectById(2L)).thenReturn(createHealthyAccount(2L));

        Long accountId = accountSelector.selectSendAccount("tenant", 100L, ChannelType.WECHAT_PERSONAL, "backup");

        assertEquals(2L, accountId);
    }

    private ChannelAccountConversation createBinding(Long accountId, int bindingType, int sortOrder) {
        ChannelAccountConversation binding = new ChannelAccountConversation();
        binding.setAccountId(accountId);
        binding.setBindingType(bindingType);
        binding.setSortOrder(sortOrder);
        binding.setStatus(1);
        return binding;
    }

    private ChannelAccount createHealthyAccount(Long id) {
        ChannelAccount account = new ChannelAccount();
        account.setId(id);
        account.setTenantCode("tenant");
        account.setAccountType(3);
        account.setChannelType("wechat_personal");
        account.setAccountId("wxid-" + id);
        account.setStatus(1);
        account.setOnlineStatus(1);
        account.setRiskLevel(1);
        account.setLastHeartbeat(LocalDateTime.now());
        return account;
    }

    private ChannelAccount createOfflineAccount(Long id) {
        ChannelAccount account = createHealthyAccount(id);
        account.setOnlineStatus(0);
        return account;
    }

    private ChannelAccount createHighRiskAccount(Long id) {
        ChannelAccount account = createHealthyAccount(id);
        account.setRiskLevel(3);
        return account;
    }
}
