package com.mosquito.project.service;

import com.mosquito.project.dto.ShareMetricsResponse;
import com.mosquito.project.dto.ShareTrackingResponse;
import com.mosquito.project.persistence.entity.LinkClickEntity;
import com.mosquito.project.persistence.repository.ActivityRepository;
import com.mosquito.project.persistence.repository.LinkClickRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Service
public class ShareTrackingService {

    private static final Logger log = LoggerFactory.getLogger(ShareTrackingService.class);

    private final LinkClickRepository linkClickRepository;
    private final ActivityRepository activityRepository;
    private final ShareConfigService shareConfigService;

    public ShareTrackingService(LinkClickRepository linkClickRepository, ActivityRepository activityRepository, ShareConfigService shareConfigService) {
        this.linkClickRepository = linkClickRepository;
        this.activityRepository = activityRepository;
        this.shareConfigService = shareConfigService;
    }

    public ShareTrackingResponse createShareTracking(Long activityId, Long inviterUserId, String source, Map<String, String> params) {
        String trackingId = UUID.randomUUID().toString();
        String shortCode = generateShortCode();

        ShareTrackingResponse response = new ShareTrackingResponse(
            trackingId,
            shortCode,
            null,
            activityId,
            inviterUserId
        );

        log.info("Created share tracking: activityId={}, inviterUserId={}, trackingId={}, source={}",
            activityId, inviterUserId, trackingId, source);

        return response;
    }

    public void recordClick(String shortCode, String ip, String userAgent, String referer, Map<String, String> params) {
        try {
            LinkClickEntity click = new LinkClickEntity();
            click.setCode(shortCode);
            click.setIp(ip);
            click.setUserAgent(userAgent);
            click.setReferer(referer);
            click.setCreatedAt(OffsetDateTime.now());

            if (params != null) {
                click.setParams(new HashMap<>(params));
            }

            linkClickRepository.save(click);
            log.debug("Recorded click for short code: {}", shortCode);
        } catch (Exception e) {
            log.error("Failed to record click for code {}: {}", shortCode, e.getMessage());
        }
    }

    public ShareMetricsResponse getShareMetrics(Long activityId, OffsetDateTime startTime, OffsetDateTime endTime) {
        List<LinkClickEntity> clicks = linkClickRepository.findByActivityIdAndCreatedAtBetween(activityId, startTime, endTime);

        ShareMetricsResponse metrics = new ShareMetricsResponse();
        metrics.setActivityId(activityId);
        metrics.setStartTime(startTime);
        metrics.setEndTime(endTime);

        Map<String, Long> sourceCounts = new HashMap<>();
        Map<String, Long> hourlyCounts = new HashMap<>();
        long totalClicks = 0;

        for (LinkClickEntity click : clicks) {
            totalClicks++;

            String source = click.getParams() != null ? click.getParams().getOrDefault("source", "unknown") : "unknown";
            sourceCounts.merge(source, 1L, Long::sum);

            String hour = click.getCreatedAt().truncatedTo(ChronoUnit.HOURS).toString();
            hourlyCounts.merge(hour, 1L, Long::sum);
        }

        metrics.setTotalClicks(totalClicks);
        metrics.setSourceDistribution(sourceCounts);
        metrics.setHourlyDistribution(hourlyCounts);
        metrics.setUniqueVisitors(calculateUniqueVisitors(clicks));

        return metrics;
    }

    public List<Map<String, Object>> getTopShareLinks(Long activityId, int topN) {
        List<Object[]> results = linkClickRepository.findTopSharedLinksByActivityId(activityId, topN);

        List<Map<String, Object>> topLinks = new ArrayList<>();
        for (Object[] row : results) {
            Map<String, Object> link = new HashMap<>();
            link.put("shortCode", row[0]);
            link.put("clickCount", row[1]);
            link.put("inviterUserId", row[2]);
            topLinks.add(link);
        }

        return topLinks;
    }

    public Map<String, Object> getConversionFunnel(Long activityId, OffsetDateTime startTime, OffsetDateTime endTime) {
        List<LinkClickEntity> clicks = linkClickRepository.findByActivityIdAndCreatedAtBetween(activityId, startTime, endTime);

        Map<String, Object> funnel = new HashMap<>();

        long totalClicks = clicks.size();
        long withReferer = clicks.stream().filter(c -> c.getReferer() != null && !c.getReferer().isBlank()).count();
        long withUserAgent = clicks.stream().filter(c -> c.getUserAgent() != null && !c.getUserAgent().isBlank()).count();

        funnel.put("totalClicks", totalClicks);
        funnel.put("withReferer", withReferer);
        funnel.put("withUserAgent", withUserAgent);
        funnel.put("refererRate", totalClicks > 0 ? (double) withReferer / totalClicks : 0);

        Map<String, Long> refererDomains = new HashMap<>();
        for (LinkClickEntity click : clicks) {
            if (click.getReferer() != null) {
                String domain = extractDomain(click.getReferer());
                refererDomains.merge(domain, 1L, Long::sum);
            }
        }
        funnel.put("topReferers", refererDomains);

        return funnel;
    }

    private long calculateUniqueVisitors(List<LinkClickEntity> clicks) {
        Set<String> uniqueIps = new HashSet<>();
        for (LinkClickEntity click : clicks) {
            if (click.getIp() != null) {
                uniqueIps.add(click.getIp());
            }
        }
        return uniqueIps.size();
    }

    private String extractDomain(String url) {
        try {
            java.net.URL uri = new java.net.URL(url);
            return uri.getHost();
        } catch (Exception e) {
            return "unknown";
        }
    }

    private String generateShortCode() {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder(8);
        Random random = new Random();
        for (int i = 0; i < 8; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }
}
