package shop.alien.store.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import shop.alien.entity.store.CommonPushChannelConfig;
import shop.alien.entity.store.CommonPushTask;
import shop.alien.entity.store.CommonPushTaskNum;
import shop.alien.entity.store.CommonPushTaskStatus;
import shop.alien.entity.store.dto.CommonPushTaskStatsDto;
import shop.alien.mapper.CommonPushChannelConfigMapper;
import shop.alien.mapper.CommonPushTaskMapper;
import shop.alien.mapper.CommonPushTaskUserMapper;
import shop.alien.store.dto.CommonPushChannelStatsDto;
import shop.alien.store.service.CommonPushTaskNumService;
import shop.alien.store.service.CommonPushTaskStatsService;
import shop.alien.store.service.channel.CommonPushVendorHttpClient;
import shop.alien.store.util.CommonPushPhoneType;
import shop.alien.store.util.CommonPushVendorTaskIdUtil;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class CommonPushTaskStatsServiceImpl implements CommonPushTaskStatsService {

    private static final List<Integer> SYNC_STATUSES = Arrays.asList(
            CommonPushTaskStatus.SENT,
            CommonPushTaskStatus.DELIVERED
    );

    private final CommonPushTaskMapper commonPushTaskMapper;
    private final CommonPushTaskUserMapper commonPushTaskUserMapper;
    private final CommonPushTaskNumService commonPushTaskNumService;
    private final CommonPushChannelConfigMapper commonPushChannelConfigMapper;
    private final CommonPushVendorHttpClient commonPushVendorHttpClient;

    @Override
    public int syncPushTaskStatistics() {
        List<CommonPushTask> tasks = commonPushTaskMapper.selectList(new LambdaQueryWrapper<CommonPushTask>()
                .in(CommonPushTask::getStatus, SYNC_STATUSES)
                .isNotNull(CommonPushTask::getVendorTaskIds)
                .ne(CommonPushTask::getVendorTaskIds, ""));
        if (tasks == null || tasks.isEmpty()) {
            return 0;
        }
        Map<String, CommonPushChannelConfig> channelMap = loadChannelMap();
        int successCount = 0;
        for (CommonPushTask task : tasks) {
            try {
                if (syncSingleTask(task, channelMap)) {
                    successCount++;
                }
            } catch (Exception e) {
                log.error("同步推送统计失败, taskId={}, taskNo={}", task.getId(), task.getTaskNo(), e);
            }
        }
        return successCount;
    }

    private boolean syncSingleTask(CommonPushTask task, Map<String, CommonPushChannelConfig> channelMap) {
        Map<String, String> vendorTaskIds = CommonPushVendorTaskIdUtil.parse(task.getVendorTaskIds());
        if (vendorTaskIds.isEmpty()) {
            return false;
        }
        boolean updated = false;
        for (Map.Entry<String, String> entry : vendorTaskIds.entrySet()) {
            if (!entry.getKey().endsWith("_task_id")) {
                continue;
            }
            String channelCode = CommonPushPhoneType.channelCodeFromTaskIdKey(entry.getKey());
            String phoneType = CommonPushPhoneType.fromChannelCode(channelCode);
            if (StringUtils.isAnyBlank(channelCode, phoneType)) {
                continue;
            }
            CommonPushChannelConfig channelConfig = channelMap.get(channelCode);
            if (channelConfig == null) {
                log.warn("统计同步跳过：渠道未配置, taskId={}, channelCode={}", task.getId(), channelCode);
                continue;
            }
            JSONObject credential = parseCredential(channelConfig.getCredentialJson());
            if (credential == null) {
                continue;
            }
            CommonPushChannelStatsDto stats = queryChannelStats(channelCode, credential, vendorTaskIds,
                    entry.getValue(), task.getId(), phoneType);
            if (stats == null) {
                continue;
            }
            upsertTaskNum(task, phoneType, stats);
            updated = true;
        }
        return updated;
    }

    private CommonPushChannelStatsDto queryChannelStats(String channelCode, JSONObject credential,
                                                          Map<String, String> vendorTaskIds, String taskIdValue,
                                                          Long pushTaskId, String phoneType) {
        if (StringUtils.isBlank(taskIdValue)) {
            return null;
        }
        String firstTaskId = taskIdValue.split(",")[0].trim();
        switch (channelCode) {
            case "oppo": {
                String messageId = vendorTaskIds.get("oppo_message_id");
                return commonPushVendorHttpClient.queryOppoStatistics(credential, messageId, firstTaskId);
            }
            case "vivo":
                return commonPushVendorHttpClient.queryVivoStatistics(credential, firstTaskId);
            case "xiaomi":
                return commonPushVendorHttpClient.queryXiaomiStatistics(credential, firstTaskId);
            case "huawei":
                return queryHuaweiStatistics(pushTaskId, phoneType);
            case "honor":
                return queryHonorStatistics(pushTaskId, phoneType);
            case "samsung":
                return querySamsungStatistics(pushTaskId, phoneType);
            case "apns":
                return queryApnsStatistics(pushTaskId, phoneType);
            default:
                log.debug("渠道暂无统计查询接口, channelCode={}, taskId={}", channelCode, firstTaskId);
                return null;
        }
    }

    /**
     * 华为无厂商侧统计拉取 API，基于 common_push_task_user 回调数据聚合。
     */
    private CommonPushChannelStatsDto queryHuaweiStatistics(Long pushTaskId, String phoneType) {
        return aggregateCallbackStatistics(pushTaskId, phoneType, "huawei");
    }

    private CommonPushChannelStatsDto queryHonorStatistics(Long pushTaskId, String phoneType) {
        return aggregateCallbackStatistics(pushTaskId, phoneType, "honor");
    }

    private CommonPushChannelStatsDto querySamsungStatistics(Long pushTaskId, String phoneType) {
        return aggregateCallbackStatistics(pushTaskId, phoneType, "samsung");
    }

    private CommonPushChannelStatsDto queryApnsStatistics(Long pushTaskId, String phoneType) {
        return aggregateCallbackStatistics(pushTaskId, phoneType, "apns");
    }

    private CommonPushChannelStatsDto aggregateCallbackStatistics(Long pushTaskId, String phoneType,
                                                                    String channelCode) {
        if (pushTaskId == null) {
            return null;
        }
        CommonPushTaskStatsDto stats = commonPushTaskUserMapper.selectStatsByTaskAndPhoneType(pushTaskId, phoneType);
        if (isZeroStats(stats) && StringUtils.isNotBlank(phoneType)) {
            stats = commonPushTaskUserMapper.selectStatsByTaskAndPhoneType(pushTaskId, null);
        }
        if (stats == null || isZeroStats(stats)) {
            log.debug("回调统计无数据, channelCode={}, pushTaskId={}, phoneType={}", channelCode, pushTaskId, phoneType);
            return null;
        }
        CommonPushChannelStatsDto result = new CommonPushChannelStatsDto();
        result.setRealSend(longToString(stats.getSentCount()));
        result.setRealDelivered(longToString(stats.getDeliveredCount()));
        result.setShowSum(longToString(stats.getShowCount()));
        result.setClickSum(longToString(stats.getClickCount()));
        log.info("回调统计聚合完成, channelCode={}, pushTaskId={}, phoneType={}, stats={}",
                channelCode, pushTaskId, phoneType, result);
        return result;
    }

    private boolean isZeroStats(CommonPushTaskStatsDto stats) {
        return longValue(stats.getSentCount()) == 0
                && longValue(stats.getDeliveredCount()) == 0
                && longValue(stats.getShowCount()) == 0
                && longValue(stats.getClickCount()) == 0;
    }

    private long longValue(Long value) {
        return value == null ? 0L : value;
    }

    private String longToString(Long value) {
        return value == null ? null : String.valueOf(value);
    }

    private void upsertTaskNum(CommonPushTask task, String phoneType, CommonPushChannelStatsDto stats) {
        CommonPushTaskNum existing = commonPushTaskNumService.getOne(new LambdaQueryWrapper<CommonPushTaskNum>()
                .eq(CommonPushTaskNum::getPushTaskId, task.getId())
                .eq(CommonPushTaskNum::getPhoneType, phoneType)
                .last("LIMIT 1"));
        if (existing == null) {
            CommonPushTaskNum taskNum = new CommonPushTaskNum();
            taskNum.setPushTaskId(task.getId());
            taskNum.setPhoneType(phoneType);
            taskNum.setExpectedSend(task.getEstimatedCount() == null ? null : String.valueOf(task.getEstimatedCount()));
            fillStats(taskNum, stats);
            commonPushTaskNumService.save(taskNum);
            return;
        }
        fillStats(existing, stats);
        commonPushTaskNumService.updateById(existing);
    }

    private void fillStats(CommonPushTaskNum taskNum, CommonPushChannelStatsDto stats) {
        taskNum.setExpectedSend(stats.getExpectedSend());
        taskNum.setRealSend(stats.getRealSend());
        taskNum.setRealDelivered(stats.getRealDelivered());
        taskNum.setClickSum(stats.getClickSum());
        taskNum.setShowSum(stats.getShowSum());
    }

    /**
     * 推送成功后按厂商初始化 common_push_task_num 统计行。
     * <p>格式转换 vendorTaskIds key → phoneType：</p>
     * <pre>
     * 转换前 Map key: "huawei_task_id"
     * 转换后 phoneType: "3"（见 CommonPushPhoneType.HUAWEI）
     * expectedSend: task.estimatedCount 转字符串，如 "1000"
     * </pre>
     */
    @Override
    public void initTaskNumRecords(Long pushTaskId, Map<String, String> vendorTaskIds, Integer estimatedCount) {
        if (pushTaskId == null || vendorTaskIds == null || vendorTaskIds.isEmpty()) {
            return;
        }
        String expectedSend = estimatedCount == null ? null : String.valueOf(estimatedCount);
        for (Map.Entry<String, String> entry : vendorTaskIds.entrySet()) {
            if (!entry.getKey().endsWith("_task_id")) {
                continue;
            }
            String channelCode = CommonPushPhoneType.channelCodeFromTaskIdKey(entry.getKey());
            String phoneType = CommonPushPhoneType.fromChannelCode(channelCode);
            if (StringUtils.isBlank(phoneType)) {
                continue;
            }
            long count = commonPushTaskNumService.count(new LambdaQueryWrapper<CommonPushTaskNum>()
                    .eq(CommonPushTaskNum::getPushTaskId, pushTaskId)
                    .eq(CommonPushTaskNum::getPhoneType, phoneType));
            if (count > 0) {
                continue;
            }
            CommonPushTaskNum taskNum = new CommonPushTaskNum();
            taskNum.setPushTaskId(pushTaskId);
            taskNum.setPhoneType(phoneType);
            taskNum.setExpectedSend(expectedSend);
            commonPushTaskNumService.save(taskNum);
        }
    }

    private Map<String, CommonPushChannelConfig> loadChannelMap() {
        List<CommonPushChannelConfig> configs = commonPushChannelConfigMapper.selectList(
                new LambdaQueryWrapper<CommonPushChannelConfig>()
                        .eq(CommonPushChannelConfig::getEnable, 1));
        Map<String, CommonPushChannelConfig> channelMap = new java.util.HashMap<>();
        if (configs != null) {
            for (CommonPushChannelConfig config : configs) {
                if (config != null && StringUtils.isNotBlank(config.getChannelCode())) {
                    channelMap.put(StringUtils.lowerCase(config.getChannelCode().trim()), config);
                }
            }
        }
        return channelMap;
    }

    private JSONObject parseCredential(String credentialJson) {
        if (StringUtils.isBlank(credentialJson)) {
            return null;
        }
        try {
            return JSONObject.parseObject(credentialJson);
        } catch (Exception e) {
            log.warn("credential_json 解析失败: {}", e.getMessage());
            return null;
        }
    }
}
