package com.sino.mci.channel.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sino.mci.channel.adapter.registry.ChannelAdapterRegistry;
import com.sino.mci.channel.common.dto.ChannelInitRequest;
import com.sino.mci.channel.common.dto.ChannelInitResult;
import com.sino.mci.channel.common.model.AccountType;
import com.sino.mci.channel.common.model.ChannelType;
import com.sino.mci.channel.common.spi.ChannelAdapter;
import com.sino.mci.channel.dto.ChannelAccountInitRequest;
import com.sino.mci.channel.dto.CreateChannelAccountRequest;
import com.sino.mci.channel.dto.HeartbeatRequest;
import com.sino.mci.channel.dto.SyncResult;
import com.sino.mci.channel.dto.UpdateStatusRequest;
import com.sino.mci.channel.entity.ChannelAccount;
import com.sino.mci.channel.entity.ChannelSubject;
import com.sino.mci.channel.entity.SyncTask;
import com.sino.mci.channel.repository.ChannelAccountMapper;
import com.sino.mci.channel.repository.ChannelSubjectMapper;
import com.sino.mci.channel.wecom.sync.WeComSyncClient;
import com.sino.mci.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class ChannelAccountService {

    private static final String CHANNEL_WECOM = ChannelType.WECOM.name().toLowerCase();

    private static final int ONLINE_STATUS_OFFLINE = 0;
    private static final int ONLINE_STATUS_ONLINE = 1;
    private static final int ACCOUNT_STATUS_ENABLED = 1;
    private static final int RISK_LEVEL_HIGH = 80;

    private final ChannelAccountMapper accountMapper;
    private final ChannelSubjectMapper subjectMapper;
    private final WeComSyncClient weComSyncClient;
    private final ChannelAdapterRegistry adapterRegistry;
    private final SyncTaskService syncTaskService;
    private final SyncTaskExecutor syncTaskExecutor;

    public List<ChannelAccount> listAccounts(String tenantCode) {
        return accountMapper.selectList(
                new LambdaQueryWrapper<ChannelAccount>()
                        .eq(ChannelAccount::getTenantCode, tenantCode)
                        .eq(ChannelAccount::getStatus, 1)
                        .orderByDesc(ChannelAccount::getId));
    }

    public ChannelAccount getAccount(Long id, String tenantCode) {
        ChannelAccount account = accountMapper.selectById(id);
        if (account == null || !tenantCode.equals(account.getTenantCode())) {
            throw new BusinessException("渠道账号不存在: " + id);
        }
        return account;
    }

    @Transactional
    public ChannelAccount createAccount(String tenantCode, CreateChannelAccountRequest request) {
        if (request.getSubjectId() != null) {
            ChannelSubject subject = subjectMapper.selectById(request.getSubjectId());
            if (subject == null) {
                throw new BusinessException("渠道主体不存在");
            }
        }

        ChannelAccount existing = accountMapper.selectOne(
                new LambdaQueryWrapper<ChannelAccount>()
                        .eq(ChannelAccount::getTenantCode, tenantCode)
                        .eq(ChannelAccount::getChannelType, request.getChannelType())
                        .eq(ChannelAccount::getAccountId, request.getAccountId()));
        if (existing != null) {
            throw new BusinessException("渠道账号已存在: " + request.getAccountId());
        }

        ChannelAccount account = new ChannelAccount();
        account.setTenantCode(tenantCode);
        account.setAccountType(request.getAccountType());
        account.setChannelType(request.getChannelType());
        account.setSubjectId(request.getSubjectId());
        account.setAccountId(request.getAccountId());
        account.setAccountName(request.getAccountName());
        account.setSessionArchiveEnabled(request.getSessionArchiveEnabled() != null ? request.getSessionArchiveEnabled() : 0);
        account.setServerHost(request.getServerHost());
        account.setExtInfo(normalizeExtInfo(request.getExtInfo()));
        account.setLoginStatus(0);
        account.setOnlineStatus(0);
        account.setRiskLevel(0);
        account.setStatus(1);
        accountMapper.insert(account);
        return account;
    }

    @Transactional
    public ChannelAccount updateAccount(Long id, String tenantCode, CreateChannelAccountRequest request) {
        ChannelAccount account = getAccount(id, tenantCode);

        if (request.getSubjectId() != null) {
            ChannelSubject subject = subjectMapper.selectById(request.getSubjectId());
            if (subject == null) {
                throw new BusinessException("渠道主体不存在");
            }
        }

        if (!account.getAccountId().equals(request.getAccountId())) {
            ChannelAccount existing = accountMapper.selectOne(
                    new LambdaQueryWrapper<ChannelAccount>()
                            .eq(ChannelAccount::getTenantCode, tenantCode)
                            .eq(ChannelAccount::getChannelType, request.getChannelType())
                            .eq(ChannelAccount::getAccountId, request.getAccountId()));
            if (existing != null) {
                throw new BusinessException("渠道账号已存在: " + request.getAccountId());
            }
        }

        account.setAccountType(request.getAccountType());
        account.setChannelType(request.getChannelType());
        account.setSubjectId(request.getSubjectId());
        account.setAccountId(request.getAccountId());
        account.setAccountName(request.getAccountName());
        account.setSessionArchiveEnabled(request.getSessionArchiveEnabled() != null ? request.getSessionArchiveEnabled() : 0);
        account.setServerHost(request.getServerHost());
        account.setExtInfo(normalizeExtInfo(request.getExtInfo()));
        accountMapper.updateById(account);
        return account;
    }

    private String normalizeExtInfo(String extInfo) {
        return (extInfo == null || extInfo.isBlank()) ? null : extInfo;
    }

    @Transactional
    public void deleteAccount(Long id, String tenantCode) {
        ChannelAccount account = getAccount(id, tenantCode);
        account.setStatus(0);
        accountMapper.updateById(account);
    }

    @Transactional
    public ChannelAccount updateAccountStatus(Long id, String tenantCode, UpdateStatusRequest request) {
        ChannelAccount account = getAccount(id, tenantCode);
        account.setStatus(request.getStatus());
        accountMapper.updateById(account);
        return account;
    }

    public ChannelAccount getAccountStatus(Long id, String tenantCode) {
        return getAccount(id, tenantCode);
    }

    @Transactional
    public ChannelAccount heartbeat(String accountCode, HeartbeatRequest request) {
        ChannelAccount account = accountMapper.selectOne(
                new LambdaQueryWrapper<ChannelAccount>()
                        .eq(ChannelAccount::getAccountId, accountCode)
                        .eq(ChannelAccount::getStatus, ACCOUNT_STATUS_ENABLED));
        if (account == null) {
            throw new BusinessException("渠道账号不存在: " + accountCode);
        }

        account.setOnlineStatus(ONLINE_STATUS_ONLINE);
        account.setLastHeartbeat(LocalDateTime.now());
        if (request != null && request.getRiskLevel() != null) {
            account.setRiskLevel(request.getRiskLevel());
        }
        accountMapper.updateById(account);

        if (account.getRiskLevel() >= RISK_LEVEL_HIGH) {
            log.warn("账号高风险: accountId={}, riskLevel={}", account.getAccountId(), account.getRiskLevel());
        }

        return account;
    }

    @Transactional
    public int checkOfflineAccounts(int timeoutSeconds) {
        LocalDateTime threshold = LocalDateTime.now().minusSeconds(timeoutSeconds);
        List<ChannelAccount> accounts = accountMapper.selectList(
                new LambdaQueryWrapper<ChannelAccount>()
                        .eq(ChannelAccount::getOnlineStatus, ONLINE_STATUS_ONLINE)
                        .eq(ChannelAccount::getStatus, ACCOUNT_STATUS_ENABLED)
                        .and(w -> w.isNull(ChannelAccount::getLastHeartbeat)
                                .or()
                                .lt(ChannelAccount::getLastHeartbeat, threshold)));

        int offlineCount = 0;
        for (ChannelAccount account : accounts) {
            log.warn("账号离线: accountId={}, lastHeartbeat={}", account.getAccountId(), account.getLastHeartbeat());
            account.setOnlineStatus(ONLINE_STATUS_OFFLINE);
            accountMapper.updateById(account);
            offlineCount++;
        }
        return offlineCount;
    }

    @Transactional
    public ChannelInitResult initAccount(String tenantCode, ChannelAccountInitRequest request) {
        ChannelType channelType;
        try {
            channelType = ChannelType.valueOf(request.getChannelType().toUpperCase());
        } catch (IllegalArgumentException | NullPointerException e) {
            throw new BusinessException("不支持的渠道类型: " + request.getChannelType());
        }

        ChannelAdapter adapter = adapterRegistry.getAdapter(channelType);
        if (adapter == null) {
            throw new BusinessException("不支持的渠道类型: " + request.getChannelType());
        }

        ChannelAccount account = null;
        if (request.getAccountId() != null) {
            account = accountMapper.selectById(request.getAccountId());
            if (account != null && !tenantCode.equals(account.getTenantCode())) {
                account = null;
            }
        }

        Map<String, Object> config = new HashMap<>();
        if (request.getConfig() != null) {
            config.putAll(request.getConfig());
        }

        Long subjectId = request.getSubjectId();
        if (account != null) {
            config.putIfAbsent("account_id", account.getAccountId());
            config.putIfAbsent("account_name", account.getAccountName());
            if (account.getSubjectId() != null) {
                subjectId = account.getSubjectId();
            }
        }

        if (subjectId != null) {
            ChannelSubject subject = subjectMapper.selectById(subjectId);
            if (subject != null) {
                if (subject.getCorpId() != null) {
                    config.putIfAbsent("corp_id", subject.getCorpId());
                }
                if (subject.getAgentSecret() != null) {
                    config.putIfAbsent("secret", subject.getAgentSecret());
                }
                if (subject.getAgentId() != null) {
                    config.putIfAbsent("agent_id", subject.getAgentId());
                }
            }
        }

        ChannelInitRequest initRequest = new ChannelInitRequest();
        initRequest.setChannelType(channelType);
        initRequest.setAccountType(AccountType.of(request.getAccountType()));
        initRequest.setSubjectId(subjectId);
        initRequest.setConfig(config);

        ChannelInitResult result = adapter.initAccount(initRequest);
        if (!result.isSuccess()) {
            return result;
        }

        String resultAccountId = result.getAccountId();
        if (account != null) {
            account.setAccountName(result.getAccountName() != null ? result.getAccountName() : account.getAccountName());
            account.setLoginStatus(1);
            accountMapper.updateById(account);
        } else if (resultAccountId != null) {
            ChannelAccount existing = accountMapper.selectOne(
                    new LambdaQueryWrapper<ChannelAccount>()
                            .eq(ChannelAccount::getTenantCode, tenantCode)
                            .eq(ChannelAccount::getChannelType, request.getChannelType())
                            .eq(ChannelAccount::getAccountId, resultAccountId));
            if (existing != null) {
                existing.setAccountName(result.getAccountName() != null ? result.getAccountName() : existing.getAccountName());
                existing.setLoginStatus(1);
                accountMapper.updateById(existing);
            } else {
                ChannelAccount newAccount = new ChannelAccount();
                newAccount.setTenantCode(tenantCode);
                newAccount.setAccountType(request.getAccountType());
                newAccount.setChannelType(request.getChannelType());
                newAccount.setSubjectId(subjectId);
                newAccount.setAccountId(resultAccountId);
                newAccount.setAccountName(result.getAccountName() != null ? result.getAccountName() : resultAccountId);
                newAccount.setLoginStatus(1);
                newAccount.setOnlineStatus(0);
                newAccount.setRiskLevel(0);
                newAccount.setStatus(1);
                accountMapper.insert(newAccount);
            }
        }

        return result;
    }

    public SyncTask submitContactsSyncTask(Long accountId) {
        ChannelAccount account = getWeComAccount(accountId);
        SyncTask task = syncTaskService.createTask(
                account.getTenantCode(), "account_contacts", account.getChannelType(), accountId, account.getAccountId());
        syncTaskExecutor.executeAccountContactsSync(task.getId());
        return task;
    }

    public SyncTask submitConversationsSyncTask(Long accountId) {
        ChannelAccount account = getWeComAccount(accountId);
        SyncTask task = syncTaskService.createTask(
                account.getTenantCode(), "account_conversations", account.getChannelType(), accountId, account.getAccountId());
        syncTaskExecutor.executeAccountConversationsSync(task.getId());
        return task;
    }

    private ChannelAccount getWeComAccount(Long accountId) {
        ChannelAccount account = accountMapper.selectById(accountId);
        if (account == null) {
            throw new BusinessException("渠道账号不存在: " + accountId);
        }
        if (!CHANNEL_WECOM.equals(account.getChannelType())) {
            throw new BusinessException("仅支持企微渠道账号同步");
        }
        return account;
    }

}
