package com.pig4cloud.pig.marketing.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.common.core.util.ip.IPUtils;
import com.pig4cloud.pig.marketing.api.dto.data.MarketingDataReportDTO;
import com.pig4cloud.pig.marketing.api.dto.data.PageFirstLevelDataDTO;
import com.pig4cloud.pig.marketing.api.dto.data.PageSecondLevelDataDTO;
import com.pig4cloud.pig.marketing.api.entity.*;
import com.pig4cloud.pig.marketing.api.util.FingerprintCombinerUtil;
import com.pig4cloud.pig.marketing.api.util.JA4GeneratorUtil;
import com.pig4cloud.pig.marketing.api.util.JA4HGeneratorUtil;
import com.pig4cloud.pig.marketing.api.vo.config.GetMarketingGlobalConfigVO;
import com.pig4cloud.pig.marketing.api.vo.data.MarketingDataReportVO;
import com.pig4cloud.pig.marketing.api.vo.data.PageFirstLevelDataVO;
import com.pig4cloud.pig.marketing.api.vo.data.PageSecondLevelDataVO;
import com.pig4cloud.pig.marketing.mapper.*;
import com.pig4cloud.pig.marketing.service.MarketingConfigService;
import com.pig4cloud.pig.marketing.service.MarketingDataService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author: lwh
 * @date: 2025-07-22
 * @description: 营销数据service服务类
 */
@Slf4j
@Service
@AllArgsConstructor
public class MarketingDataServiceImpl implements MarketingDataService {

	private final MarketingDataMapper marketingDataMapper;

	private final MarketingAppsMapper marketingAppsMapper;

	private final MarketingAppsIpMapper marketingAppsIpMapper;

	private final MarketingGroupIpMapper marketingGroupIpMapper;

	private final MarketingAppsDomainMapper marketingAppsDomainMapper;

	private final MarketingGroupDomainMapper marketingGroupDomainMapper;

	private final MarketingConfigService marketingConfigService;

	private final RedisTemplate redisTemplate;


	/**
	 * 上报营销数据
	 * @param reqDto 营销数据
	 * @param request request
	 * @return MarketingDataReportVO
	 */
	@Override
	public MarketingDataReportVO reportMarketingData(MarketingDataReportDTO reqDto, HttpServletRequest request) {

		// 获取全局配置
		GetMarketingGlobalConfigVO config = marketingConfigService.getMarketingGlobalConfig();

		String domain = getDomainFromRequest(request);

		MarketingDataReportVO result = new MarketingDataReportVO();
		BeanUtils.copyProperties(config,  result);
		result.setType("link");

		// 获取客户端IP地址
		String ip = IPUtils.getIpAddress(request);
		// 获取域名
//		String domain = reqDto.getDomain();
		// 应用匹配
		MarketingApps marketingApps = matchedApp(ip, domain);

		// 生成指纹
		String backend = generateFingerprint(request);
		String fingerprint = FingerprintCombinerUtil.combine(reqDto.getFingerprint(), backend);

		result.setFingerprint(backend);
		result.setCombine(fingerprint);

		log.info("ip:{}---domain:{}", ip, domain);
		log.info("前端指纹：{}", reqDto.getFingerprint());
		log.info("后端指纹：{}", backend);
		log.info("组合指纹：{}", fingerprint);
		// 未匹配到应用，返回默认信息
		if (marketingApps == null){
			result.setUrl(getTriggeredUrl(ip, fingerprint,config.getUrl(), config.getTriggerNum()));
			return result;
		}


		// 入库
		MarketingData marketingData = new MarketingData();
		BeanUtils.copyProperties(reqDto, marketingData);
		marketingData.setFingerprint(fingerprint);
		marketingData.setIp( ip);
		marketingData.setAppId(marketingApps.getAppId());
		marketingDataMapper.insert(marketingData);

		// 计算是否达到频率
		result.setAppId(marketingApps.getAppId());
		result.setUrl(getTriggeredUrl(ip, fingerprint,marketingApps.getAppUrl(), marketingApps.getTriggerNum()));

		// 返回结果

		return result;
	}

	/**
	 * 从HttpServletRequest中提取域名信息
	 * @param request 请求对象
	 * @return 域名（如example.com），异常时返回null
	 */
	private String getDomainFromRequest(HttpServletRequest request) {
		try {
			// 1. 优先从请求的服务器名称获取（包含端口时需处理）
			String serverName = request.getServerName();
			if (serverName != null && !serverName.trim().isEmpty()) {
				return serverName.trim();
			}

			// 2. 若serverName为空，尝试从Host头获取（可能包含端口，如example.com:8080）
			String hostHeader = request.getHeader("Host");
			if (hostHeader != null && !hostHeader.trim().isEmpty()) {
				// 去除端口部分（若有）
				int colonIndex = hostHeader.indexOf(':');
				return colonIndex != -1 ? hostHeader.substring(0, colonIndex).trim() : hostHeader.trim();
			}

			// 3. 若Host头也为空，尝试从请求URL中解析
			String requestUrl = request.getRequestURL().toString();
			if (requestUrl != null && !requestUrl.trim().isEmpty()) {
				// 解析URL（如https://example.com/path → 提取example.com）
				java.net.URL url = new java.net.URL(requestUrl);
				return url.getHost();
			}
		} catch (Exception e) {
			// 记录异常，但不影响主流程
			log.error("获取域名信息失败", e);
		}

		// 所有方式失败时，返回默认值或null
		return null;
	}

	/**
	 * 根据请求协议类型生成对应指纹
	 * @param request HTTP请求对象
	 * @return 包含指纹和指纹类型的Map，key分别为"fingerprint"和"fingerprintType"
	 */
	private String generateFingerprint(HttpServletRequest request) {
		String fingerprint = null;
		String fingerprintType = null;
		String scheme = request.getScheme().toLowerCase();

		try {
			if ("https".equals(scheme)) {
				// HTTPS请求 - 生成JA4指纹
				JA4GeneratorUtil ja4GeneratorUtil = new JA4GeneratorUtil();
				fingerprint = ja4GeneratorUtil.generateJA4(request);
				fingerprintType = "ja4";
			} else if ("http".equals(scheme)) {
				// HTTP请求 - 生成JA4H指纹
				// 提取HTTP请求信息
				String method = request.getMethod();
				String path = request.getRequestURI();
				String httpVersion = request.getProtocol();

				// 提取请求头
				Map<String, String> headers = new HashMap<>();
				Enumeration<String> headerNames = request.getHeaderNames();
				if (headerNames != null) {
					while (headerNames.hasMoreElements()) {
						String headerName = headerNames.nextElement();
						headers.put(headerName, request.getHeader(headerName));
					}
				}
				headers.remove("Origin");
				headers.remove("Referer");
				headers.remove("REQUEST-START-TIME");
				headers.remove("Content-Length");

				// 生成JA4H指纹
				JA4HGeneratorUtil ja4HGeneratorUtil = new JA4HGeneratorUtil();
				fingerprint = ja4HGeneratorUtil.generateJA4H(method, path, httpVersion, headers);
				fingerprintType = "ja4h";
			}
		} catch (Exception e) {
			// 指纹生成失败时记录日志，但不影响主流程
			log.error("生成指纹失败:{}", e.getMessage());
		}

		return fingerprint;
	}

	/**
	 * 分页统计一级营销数据
	 * @param reqDto 分页参数
	 * @return Page
	 */
	@Override
	public Page<PageFirstLevelDataVO> pageFirstLevelData(PageFirstLevelDataDTO reqDto) {
		String ip = reqDto.getIp();
		// 计算时间条件
		LocalDateTime now = LocalDateTime.now();
		LocalDateTime lastHourTime = now.minusHours(1);
		LocalDateTime todayStartTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);

		// 格式化时间
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		String lastHourTimeStr = lastHourTime.format(formatter);
		String todayStartTimeStr = todayStartTime.format(formatter);

		// 计算偏移量
		long offset = (reqDto.getCurrent() - 1) * reqDto.getSize();

		// 查询数据
		Page<PageFirstLevelDataVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());

		List<PageFirstLevelDataVO> rs = marketingDataMapper.countFirstLevelMarketingData(ip, reqDto.getAppId(), lastHourTimeStr, todayStartTimeStr, offset, reqDto.getSize());
		page.setRecords(rs);
		page.setTotal(marketingDataMapper.countFirstLevelDataTotal(ip, reqDto.getAppId()));

		return page;
	}

	/**
	 * 分页统计二级营销数据
	 * @param reqDto 分页参数
	 * @return Page
	 */
	@Override
	public Page<PageSecondLevelDataVO> pageSecondLevelData(PageSecondLevelDataDTO reqDto) {
		String ip = reqDto.getIp();

		// 计算时间条件
		LocalDateTime now = LocalDateTime.now();
		LocalDateTime lastHourTime = now.minusHours(1);
		LocalDateTime todayStartTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);

		// 格式化时间
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		String lastHourTimeStr = lastHourTime.format(formatter);
		String todayStartTimeStr = todayStartTime.format(formatter);

		// 计算偏移量
		long offset = (reqDto.getCurrent() - 1) * reqDto.getSize();

		// 查询数据
		Page<PageSecondLevelDataVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
		page.setRecords(marketingDataMapper.countSecondLevelMarketingData(ip, lastHourTimeStr, todayStartTimeStr, offset, reqDto.getSize()));
		page.setTotal(marketingDataMapper.countSecondLevelDataTotal(ip));

		return page;
	}


	/**
	 * 获取匹配的app
	 * @param ip ip
	 * @param domain 域名
	 * @return MarketingApps
	 */
	private MarketingApps matchedApp(String ip, String domain){
		// 根据IP和域名查询跳转链接
		// 1、筛选营销投放开启、域名限制开启的应用
		// 1.1、 查询ip表和ip分组表中，包含该ip的应用id列表，条件为应用的营销投放开启、域名限制开启
		List<Long> ipMatchedAppIds = getIpMatchedAppIds(ip, true);

		if (!ipMatchedAppIds.isEmpty()) {
			// 1.2 查询域名匹配的应用ID列表
			List<Long> domainMatchedAppIds = getDomainMatchedAppIds(domain, ipMatchedAppIds);
			if (!domainMatchedAppIds.isEmpty()) {
				// 1.3 查找最新创建的应用
				return getLatestMarketingApp(domainMatchedAppIds);
			}
		}
		// 2、筛选营销投放开启、域名限制关闭的应用
		List<Long> ipMatchedAppIdsWithoutDomainLimit = getIpMatchedAppIds(ip, false);
		if (!ipMatchedAppIdsWithoutDomainLimit.isEmpty()) {
			return getLatestMarketingApp(ipMatchedAppIdsWithoutDomainLimit);
		}
		return null;
	}

	/**
	 * 根据triggerNum和Redis计数决定是否返回URL
	 */
	private String getTriggeredUrl(String ip, String fingerprint, String url, String num) {

		if (fingerprint == null || ip == null) {
			return "";
		}

		BigDecimal triggerNum = new BigDecimal(num)
				.setScale(2, RoundingMode.HALF_UP);
		String countKey = String.format("marketing:trigger:count:%s:%s", fingerprint, ip);

		// 1. 概率模式（triggerNum < 1）
		if (triggerNum.compareTo(BigDecimal.ONE) < 0) {
			// 生成0-1之间的随机数与triggerNum比较
			double random = Math.random();
			return random <= triggerNum.doubleValue() ? url : "";
		}

		// 2. 每次返回（triggerNum = 1）
		if (triggerNum.compareTo(BigDecimal.ONE) == 0) {
			return url;
		}

		// 3. 周期模式（triggerNum > 1）
		// 向上取整转换为整数周期（如2.3 → 3，3.9 → 4）
		int cycle = triggerNum.setScale(0, RoundingMode.CEILING).intValue();
		if (cycle < 1) {
			return "";
		}

		// Redis原子自增计数
		Long currentCount = redisTemplate.opsForValue().increment(countKey);
		if (currentCount == null) {
			return "";
		}

		// 首次访问设置过期时间（24小时）
		if (currentCount == 1) {
			redisTemplate.expire(countKey, 24, TimeUnit.HOURS);
		}

		// 达到周期次数时返回URL并重置计数
		if (currentCount % cycle == 0) {
			redisTemplate.delete(countKey);
			return url;
		}
		return "";
	}

	/**
	 * 根据IP获取匹配的应用ID列表
	 * @param ip IP地址
	 * @param domainLimit 是否开启域名限制
	 * @return 应用ID列表
	 */
	private List<Long> getIpMatchedAppIds(String ip, boolean domainLimit) {
		// 查询营销投放开启、域名限制符合条件的应用ID
		List<MarketingApps> qualifiedApps = marketingAppsMapper.selectList(Wrappers.<MarketingApps>lambdaQuery()
				.eq(MarketingApps::getLaunch, true)
				.eq(MarketingApps::getDomainLimit, domainLimit)
		);
		if (qualifiedApps.isEmpty()) {
			return Collections.emptyList();
		}
		List<Long> qualifiedAppIds = qualifiedApps.stream()
				.map(MarketingApps::getId)
				.collect(Collectors.toList());

		// 转换IP为长整型用于比较
		long ipLong = ipToLong(ip);

		// 1. 从marketing_apps_ip表查询直接关联的IP匹配
		QueryWrapper<MarketingAppsIp> directIpQuery = new QueryWrapper<>();
		directIpQuery.eq("ip_type",1)
				.in("app_id", qualifiedAppIds)
				.and(wrapper -> wrapper
						// 单IP匹配
						.eq("ip_mode", 1).eq("start_ip", ip)
						// IP段匹配：将数据库字符串IP转换为长整型后比较
						.or().eq("ip_mode", 2)
						.le("INET_ATON(start_ip)", ipLong)
						.ge("INET_ATON(end_ip)", ipLong)
				);
		List<MarketingAppsIp> directIpMatches = marketingAppsIpMapper.selectList(directIpQuery);
		Set<Long> appIdSet = directIpMatches.stream()
				.map(MarketingAppsIp::getAppId)
				.collect(Collectors.toSet());

		// 2. 从分组查询IP匹配的应用
		// 2.1 查询包含该IP的分组ID
		QueryWrapper<MarketingGroupIp> groupIpQuery = new QueryWrapper<>();
		groupIpQuery.and(qw -> qw
						// 单IP匹配
						.eq("ip_mode", 1).eq("start_ip", ip)
						// IP段匹配：转换为长整型后比较
						.or().eq("ip_mode", 2)
						.le("INET_ATON(start_ip)", ipLong)
						.ge("INET_ATON(end_ip)", ipLong)
				);
		List<MarketingGroupIp> groupIpMatches = marketingGroupIpMapper.selectList(groupIpQuery);
		if (!groupIpMatches.isEmpty()) {
			List<Long> groupIds = groupIpMatches.stream()
					.map(MarketingGroupIp::getGroupId)
					.collect(Collectors.toList());

			// 2.2 查询关联这些分组的应用
			List<MarketingAppsIp> groupAppMatches = marketingAppsIpMapper.selectList(Wrappers.<MarketingAppsIp>lambdaQuery()
					.in(MarketingAppsIp::getGroupId, groupIds)
					.eq(MarketingAppsIp::getSourceType, 1) // 来源类型为分组
					.eq(MarketingAppsIp::getIpType, 1)	// 白名单
					.in(MarketingAppsIp::getAppId, qualifiedAppIds)
			);
			groupAppMatches.forEach(appIp -> appIdSet.add(appIp.getAppId()));
		}

		return new ArrayList<>(appIdSet);
	}

	/**
	 * 根据域名获取匹配的应用ID列表
	 * @param domain 域名
	 * @param appIdList 待筛选的应用ID列表
	 * @return 匹配的应用ID列表
	 */
	private List<Long> getDomainMatchedAppIds(String domain, List<Long> appIdList) {
		Set<Long> matchedAppIds = new HashSet<>();

		// 1. 从marketing_apps_domain表查询直接关联的域名匹配
		List<MarketingAppsDomain> directDomainMatches = marketingAppsDomainMapper.selectList(Wrappers.<MarketingAppsDomain>lambdaQuery()
				.in(MarketingAppsDomain::getAppId, appIdList)
				.eq(MarketingAppsDomain::getSourceType, 2)
				.eq(MarketingAppsDomain::getDomain, domain)
		);
		directDomainMatches.forEach(domainApp -> matchedAppIds.add(domainApp.getAppId()));

		// 2. 从分组查询域名匹配的应用
		// 2.1 查询包含该域名的分组ID
		List<MarketingGroupDomain> groupDomainMatches = marketingGroupDomainMapper.selectList(Wrappers.<MarketingGroupDomain>lambdaQuery()
				.eq(MarketingGroupDomain::getDomain, domain)
		);
		if (!groupDomainMatches.isEmpty()) {
			List<Long> groupIds = groupDomainMatches.stream()
					.map(MarketingGroupDomain::getGroupId)
					.collect(Collectors.toList());

			// 2.2 查询关联这些分组的应用
			List<MarketingAppsDomain> groupAppMatches = marketingAppsDomainMapper.selectList(Wrappers.<MarketingAppsDomain>lambdaQuery()
					.in(MarketingAppsDomain::getGroupId, groupIds)
					.eq(MarketingAppsDomain::getSourceType, 1)
					.in(MarketingAppsDomain::getAppId, appIdList)
			);
			groupAppMatches.forEach(domainApp -> matchedAppIds.add(domainApp.getAppId()));
		}
		return new ArrayList<>(matchedAppIds);
	}

	/**
	 * 获取最新创建的应用
	 * @param appIds 应用ID列表
	 * @return 最新创建的应用
	 */
	private MarketingApps getLatestMarketingApp(List<Long> appIds) {
		if (appIds.isEmpty()) {
			return null;
		}

		MarketingApps marketingApps = marketingAppsMapper.selectOne(Wrappers.<MarketingApps>lambdaQuery()
				.in(MarketingApps::getId, appIds)
				.orderByDesc(MarketingApps::getCreateTime)
				.last("LIMIT 1")
		);
		return marketingApps;
	}


	/**
	 * IP地址转长整型
	 * @param ip IP地址
	 * @return 长整型IP
	 */
	private long ipToLong(String ip) {
		String[] parts = ip.split("\\.");
		long result = 0;
		for (int i = 0; i < 4; i++) {
			result |= Long.parseLong(parts[i]) << (24 - i * 8);
		}
		return result & 0xFFFFFFFFL;
	}

}
