using System.Security.Claims;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
using TrackQrApi.Data;
using TrackQrApi.Features.Analytics.Common;
using TrackQrApi.Features.Auth.Common;
using TrackQrApi.Models;

namespace TrackQrApi.Features.Analytics.Endpoints;

public class WorkspaceAnalyticsRequest
{
    public Guid WorkspaceId { get; set; }
    public string? Period { get; set; } // 24h, 7d, 30d, or null for all time
    public DateTime? StartDate { get; set; } // Custom date range start
    public DateTime? EndDate { get; set; } // Custom date range end
}

public class WorkspaceAnalyticsEndpoint(AppDbContext db)
    : Endpoint<WorkspaceAnalyticsRequest, WorkspaceAnalyticsResponse>
{
    public override void Configure()
    {
        Get("/workspaces/{WorkspaceId}/analytics");
    }

    public override async Task HandleAsync(WorkspaceAnalyticsRequest req, CancellationToken ct)
    {
        var userId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);

        // Verify workspace ownership
        var workspaceExists = await db.Workspaces
            .AnyAsync(w => w.Id == req.WorkspaceId && w.OwnerUserId == userId, ct);

        if (!workspaceExists)
        {
            await HttpContext.Response.SendAsync(new MessageResponse("Workspace not found"), 404, cancellation: ct);
            return;
        }

        // Determine time filter (custom range takes precedence over period)
        DateTime? startDate = null;
        DateTime? endDate = null;

        if (req.StartDate.HasValue && req.EndDate.HasValue)
        {
            startDate = req.StartDate.Value;
            endDate = req.EndDate.Value.AddDays(1); // Include the entire end day
        }
        else
        {
            startDate = GetStartDate(req.Period);
        }

        // Query events
        var eventsQuery = db.Events
            .Where(e => e.WorkspaceId == req.WorkspaceId);

        if (startDate.HasValue) eventsQuery = eventsQuery.Where(e => e.Timestamp >= startDate.Value);

        if (endDate.HasValue) eventsQuery = eventsQuery.Where(e => e.Timestamp < endDate.Value);

        var events = await eventsQuery.ToListAsync(ct);
        var totalEvents = events.Count;

        // Get summary
        var summary = new AnalyticsSummary(
            events.Count(e => e.Type == EventType.Click),
            events.Count(e => e.Type == EventType.Scan),
            events.Select(e => e.IpHash).Distinct().Count(),
            events.Count > 0 ? events.Min(e => e.Timestamp) : null,
            events.Count > 0 ? events.Max(e => e.Timestamp) : null
        );

        // Get time series
        var timeSeries = events
            .GroupBy(e => e.Timestamp.Date)
            .OrderBy(g => g.Key)
            .Select(g => new TimeSeriesPoint(
                g.Key,
                g.Count(e => e.Type == EventType.Click),
                g.Count(e => e.Type == EventType.Scan)
            ))
            .ToList();

        // Get top links - group events by link and get slugs
        var linkIds = events.Select(e => e.ShortLinkId).Distinct().ToList();
        var linkSlugs = await db.ShortLinks
            .Where(l => linkIds.Contains(l.Id))
            .Select(l => new { l.Id, l.Slug })
            .ToDictionaryAsync(l => l.Id, l => l.Slug, ct);

        var topLinks = events
            .GroupBy(e => e.ShortLinkId)
            .OrderByDescending(g => g.Count())
            .Take(10)
            .Select(g => new BreakdownItem(
                linkSlugs.GetValueOrDefault(g.Key, "unknown"),
                g.Count(),
                totalEvents > 0 ? Math.Round((double)g.Count() / totalEvents * 100, 1) : 0
            ))
            .ToList();

        // Get device breakdown
        var deviceBreakdown = events
            .Where(e => !string.IsNullOrEmpty(e.DeviceType))
            .GroupBy(e => e.DeviceType!)
            .OrderByDescending(g => g.Count())
            .Select(g => new BreakdownItem(
                g.Key,
                g.Count(),
                totalEvents > 0 ? Math.Round((double)g.Count() / totalEvents * 100, 1) : 0
            ))
            .ToList();

        // Get referrer breakdown
        var referrerBreakdown = events
            .Where(e => !string.IsNullOrEmpty(e.Referrer))
            .GroupBy(e => ExtractDomain(e.Referrer!))
            .OrderByDescending(g => g.Count())
            .Take(10)
            .Select(g => new BreakdownItem(
                g.Key,
                g.Count(),
                totalEvents > 0 ? Math.Round((double)g.Count() / totalEvents * 100, 1) : 0
            ))
            .ToList();

        // Get country breakdown
        var countryBreakdown = events
            .Where(e => !string.IsNullOrEmpty(e.CountryCode))
            .GroupBy(e => e.CountryCode!)
            .OrderByDescending(g => g.Count())
            .Take(10)
            .Select(g => new BreakdownItem(
                g.Key,
                g.Count(),
                totalEvents > 0 ? Math.Round((double)g.Count() / totalEvents * 100, 1) : 0
            ))
            .ToList();

        var response = new WorkspaceAnalyticsResponse(
            summary,
            timeSeries,
            topLinks,
            deviceBreakdown,
            referrerBreakdown,
            countryBreakdown
        );

        await HttpContext.Response.SendAsync(response, cancellation: ct);
    }

    private static DateTime? GetStartDate(string? period)
    {
        return period?.ToLower() switch
        {
            "24h" => DateTime.UtcNow.AddHours(-24),
            "7d" => DateTime.UtcNow.AddDays(-7),
            "30d" => DateTime.UtcNow.AddDays(-30),
            _ => null
        };
    }

    private static string ExtractDomain(string url)
    {
        try
        {
            var uri = new Uri(url);
            return uri.Host;
        }
        catch
        {
            return url.Length > 50 ? url[..50] : url;
        }
    }
}