package shop.alien.store.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import shop.alien.entity.result.R;
import shop.alien.entity.store.OceanEngineClickMonitorLog;
import shop.alien.mapper.OceanEngineClickMonitorLogMapper;
import shop.alien.store.dto.OceanEngineClickBindUserDto;
import shop.alien.store.service.OceanEngineClickMonitorLogService;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 巨量引擎点击监测服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OceanEngineClickMonitorLogServiceImpl
        extends ServiceImpl<OceanEngineClickMonitorLogMapper, OceanEngineClickMonitorLog>
        implements OceanEngineClickMonitorLogService {

    @Override
    public R<Long> receiveClickMonitor(HttpServletRequest request) {
        Map<String, String> paramMap = extractQueryParams(request);
        log.info("OceanEngineClickMonitorLogServiceImpl.receiveClickMonitor, paramCount={}, queryString={}",
                paramMap.size(), request.getQueryString());

        String clickId = firstNonBlank(paramMap,
                "clickid", "click_id", "CLICKID");
        String callbackParam = firstNonBlank(paramMap,
                "callback", "callback_param", "CALLBACK_PARAM");
        String requestId = firstNonBlank(paramMap,
                "request_id", "req_id", "REQUEST_ID");

        // 幂等：相同 click_id 或 request_id 已存在则直接返回
        OceanEngineClickMonitorLog existing = findExistingLog(clickId, requestId);
        if (existing != null) {
            log.info("OceanEngineClickMonitorLogServiceImpl.receiveClickMonitor, duplicate click, id={}, clickId={}",
                    existing.getId(), clickId);
            return R.data(existing.getId(), "重复点击，已忽略");
        }

        OceanEngineClickMonitorLog clickLog = buildClickLog(request, paramMap, clickId, callbackParam, requestId);
        boolean saved = this.save(clickLog);
        if (!saved) {
            log.error("OceanEngineClickMonitorLogServiceImpl.receiveClickMonitor, save failed, clickId={}", clickId);
            return R.fail("点击监测日志保存失败");
        }
        log.info("OceanEngineClickMonitorLogServiceImpl.receiveClickMonitor, saved id={}, clickId={}, advertiserId={}",
                clickLog.getId(), clickLog.getClickId(), clickLog.getAdvertiserId());
        return R.data(clickLog.getId(), "接收成功");
    }

    @Override
    public R<String> bindUser(OceanEngineClickBindUserDto bindDto) {
        log.info("OceanEngineClickMonitorLogServiceImpl.bindUser, param={}", bindDto);
        if (bindDto == null || bindDto.getUserId() == null) {
            return R.fail("userId不能为空");
        }
        if (StringUtils.isAllBlank(bindDto.getClickId(), bindDto.getCallbackParam())) {
            return R.fail("clickId与callbackParam至少填一个");
        }

        OceanEngineClickMonitorLog clickLog = findClickLog(bindDto.getClickId(), bindDto.getCallbackParam());
        if (clickLog == null) {
            log.warn("OceanEngineClickMonitorLogServiceImpl.bindUser, click log not found, clickId={}, callbackParam={}",
                    bindDto.getClickId(), bindDto.getCallbackParam());
            return R.fail("未找到对应的点击监测记录");
        }

        clickLog.setUserId(bindDto.getUserId());
        clickLog.setStoreId(bindDto.getStoreId());
        clickLog.setBindStatus(OceanEngineClickMonitorLog.BIND_STATUS_BOUND);
        clickLog.setBindTime(new Date());
        boolean updated = this.updateById(clickLog);
        return updated ? R.success("绑定成功") : R.fail("绑定失败");
    }

    @Override
    public R<OceanEngineClickMonitorLog> getInfoById(Long id) {
        log.info("OceanEngineClickMonitorLogServiceImpl.getInfoById, id={}", id);
        return R.data(this.getById(id));
    }

    @Override
    public R<IPage<OceanEngineClickMonitorLog>> list(Integer pageNum, Integer pageSize,
                                                     String clickId, String advertiserId,
                                                     Integer userId, Integer bindStatus) {
        log.info("OceanEngineClickMonitorLogServiceImpl.list, pageNum={}, pageSize={}, clickId={}, advertiserId={}, userId={}, bindStatus={}",
                pageNum, pageSize, clickId, advertiserId, userId, bindStatus);
        Page<OceanEngineClickMonitorLog> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<OceanEngineClickMonitorLog> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(clickId)) {
            wrapper.eq(OceanEngineClickMonitorLog::getClickId, clickId);
        }
        if (StringUtils.isNotBlank(advertiserId)) {
            wrapper.eq(OceanEngineClickMonitorLog::getAdvertiserId, advertiserId);
        }
        if (userId != null) {
            wrapper.eq(OceanEngineClickMonitorLog::getUserId, userId);
        }
        if (bindStatus != null) {
            wrapper.eq(OceanEngineClickMonitorLog::getBindStatus, bindStatus);
        }
        wrapper.orderByDesc(OceanEngineClickMonitorLog::getCreatedTime);
        return R.data(this.page(page, wrapper));
    }

    /**
     * 从 HttpServletRequest 提取全部 Query 参数
     */
    private Map<String, String> extractQueryParams(HttpServletRequest request) {
        Map<String, String> paramMap = new LinkedHashMap<>();
        Enumeration<String> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            paramMap.put(name, request.getParameter(name));
        }
        return paramMap;
    }

    /**
     * 构建点击监测日志实体
     */
    private OceanEngineClickMonitorLog buildClickLog(HttpServletRequest request,
                                                     Map<String, String> paramMap,
                                                     String clickId,
                                                     String callbackParam,
                                                     String requestId) {
        OceanEngineClickMonitorLog clickLog = new OceanEngineClickMonitorLog();
        clickLog.setClickId(clickId);
        clickLog.setCallbackParam(callbackParam);
        clickLog.setRequestId(requestId);
        clickLog.setAdvertiserId(firstNonBlank(paramMap, "advertiser_id", "ADVERTISER_ID"));
        clickLog.setCampaignId(firstNonBlank(paramMap, "campaign_id", "CAMPAIGN_ID"));
        clickLog.setAid(firstNonBlank(paramMap, "aid", "adid", "AID"));
        clickLog.setCid(firstNonBlank(paramMap, "cid", "creativeid", "CID"));
        clickLog.setProjectId(firstNonBlank(paramMap, "project_id", "PROJECT_ID"));
        clickLog.setPromotionId(firstNonBlank(paramMap, "promotion_id", "PROMOTION_ID"));
        clickLog.setConvertId(firstNonBlank(paramMap, "convert_id", "CONVERT_ID"));
        clickLog.setCsite(firstNonBlank(paramMap, "csite", "CSITE"));
        clickLog.setUnionSite(firstNonBlank(paramMap, "union_site", "UNION_SITE"));
        clickLog.setMid1(firstNonBlank(paramMap, "mid1", "MID1"));
        clickLog.setMid2(firstNonBlank(paramMap, "mid2", "MID2"));
        clickLog.setMid3(firstNonBlank(paramMap, "mid3", "MID3"));
        clickLog.setMid4(firstNonBlank(paramMap, "mid4", "MID4"));
        clickLog.setMid5(firstNonBlank(paramMap, "mid5", "MID5"));
        clickLog.setMid6(firstNonBlank(paramMap, "mid6", "MID6"));
        clickLog.setClickTs(parseLong(firstNonBlank(paramMap, "ts", "TIMESTAMP", "TS")));
        clickLog.setClickTsMs(parseLong(firstNonBlank(paramMap, "ts_ms", "TS_MS")));
        clickLog.setClientIp(firstNonBlank(paramMap, "ip", "IP"));
        clickLog.setClientIpv4(firstNonBlank(paramMap, "ipv4", "IPV4"));
        clickLog.setClientIpv6(firstNonBlank(paramMap, "ipv6", "IPV6"));
        clickLog.setUserAgent(firstNonBlank(paramMap, "ua", "UA"));
        clickLog.setOsType(firstNonBlank(paramMap, "os", "OS"));
        clickLog.setDeviceModel(firstNonBlank(paramMap, "model", "MODEL"));
        clickLog.setIdfa(firstNonBlank(paramMap, "idfa", "IDFA"));
        clickLog.setImei(firstNonBlank(paramMap, "imei", "IMEI"));
        clickLog.setOaid(firstNonBlank(paramMap, "oaid", "OAID"));
        clickLog.setOaidMd5(firstNonBlank(paramMap, "oaid_md5", "OAID_MD5"));
        clickLog.setAndroidId(firstNonBlank(paramMap, "android_id", "ANDROIDID", "ANDROIDID1"));
        clickLog.setAdVersion(resolveAdVersion(clickLog));
        clickLog.setBindStatus(OceanEngineClickMonitorLog.BIND_STATUS_UNBOUND);
        clickLog.setMonitorUrl(buildMonitorUrl(request));
        clickLog.setRawParams(JSONObject.toJSONString(paramMap));
        clickLog.setServerIp(resolveClientIp(request));
        return clickLog;
    }

    /**
     * 推断广告版本：2.0 有 project_id/promotion_id，1.0 有 aid/cid/campaign_id
     */
    private Integer resolveAdVersion(OceanEngineClickMonitorLog clickLog) {
        if (StringUtils.isNotBlank(clickLog.getProjectId())
                || StringUtils.isNotBlank(clickLog.getPromotionId())) {
            return OceanEngineClickMonitorLog.AD_VERSION_V2;
        }
        if (StringUtils.isNotBlank(clickLog.getAid())
                || StringUtils.isNotBlank(clickLog.getCid())
                || StringUtils.isNotBlank(clickLog.getCampaignId())) {
            return OceanEngineClickMonitorLog.AD_VERSION_V1;
        }
        return null;
    }

    private OceanEngineClickMonitorLog findExistingLog(String clickId, String requestId) {
        if (StringUtils.isNotBlank(clickId)) {
            OceanEngineClickMonitorLog byClickId = this.getOne(new LambdaQueryWrapper<OceanEngineClickMonitorLog>()
                    .eq(OceanEngineClickMonitorLog::getClickId, clickId)
                    .last("LIMIT 1"));
            if (byClickId != null) {
                return byClickId;
            }
        }
        if (StringUtils.isNotBlank(requestId)) {
            return this.getOne(new LambdaQueryWrapper<OceanEngineClickMonitorLog>()
                    .eq(OceanEngineClickMonitorLog::getRequestId, requestId)
                    .last("LIMIT 1"));
        }
        return null;
    }

    private OceanEngineClickMonitorLog findClickLog(String clickId, String callbackParam) {
        if (StringUtils.isNotBlank(clickId)) {
            OceanEngineClickMonitorLog log = this.getOne(new LambdaQueryWrapper<OceanEngineClickMonitorLog>()
                    .eq(OceanEngineClickMonitorLog::getClickId, clickId)
                    .orderByDesc(OceanEngineClickMonitorLog::getCreatedTime)
                    .last("LIMIT 1"));
            if (log != null) {
                return log;
            }
        }
        if (StringUtils.isNotBlank(callbackParam)) {
            return this.getOne(new LambdaQueryWrapper<OceanEngineClickMonitorLog>()
                    .eq(OceanEngineClickMonitorLog::getCallbackParam, callbackParam)
                    .orderByDesc(OceanEngineClickMonitorLog::getCreatedTime)
                    .last("LIMIT 1"));
        }
        return null;
    }

    private String buildMonitorUrl(HttpServletRequest request) {
        String queryString = request.getQueryString();
        if (StringUtils.isBlank(queryString)) {
            return request.getRequestURL().toString();
        }
        return request.getRequestURL() + "?" + queryString;
    }

    /**
     * 解析客户端真实 IP（兼容反向代理）
     */
    private String resolveClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip.split(",")[0].trim();
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    private String firstNonBlank(Map<String, String> paramMap, String... keys) {
        for (String key : keys) {
            String value = paramMap.get(key);
            if (StringUtils.isNotBlank(value)) {
                return value;
            }
        }
        return null;
    }

    private Long parseLong(String value) {
        if (StringUtils.isBlank(value)) {
            return null;
        }
        try {
            return Long.parseLong(value);
        } catch (NumberFormatException ex) {
            log.warn("OceanEngineClickMonitorLogServiceImpl.parseLong, invalid number={}", value);
            return null;
        }
    }
}
