using Microsoft.EntityFrameworkCore;
using TrackQrApi.Data;
using TrackQrApi.Models;

namespace TrackQrApi.Features.Plans.Services;

public interface IPlanLimitsService
{
    PlanLimits GetLimits(WorkspacePlan plan);
    Task<UsageStats> GetUsageAsync(Guid userId, CancellationToken ct = default);
    Task<WorkspaceUsageStats> GetWorkspaceUsageAsync(Guid workspaceId, CancellationToken ct = default);
    Task<bool> CanCreateWorkspaceAsync(Guid userId, CancellationToken ct = default);
    Task<bool> CanCreateLinkAsync(Guid workspaceId, CancellationToken ct = default);
    Task<bool> CanCreateQRCodeAsync(Guid workspaceId, CancellationToken ct = default);
    Task<bool> CanCreateDomainAsync(Guid workspaceId, CancellationToken ct = default);
    Task<bool> CanTrackEventAsync(Guid workspaceId, CancellationToken ct = default);
}

public record PlanLimits(
    int MaxWorkspaces,
    int MaxLinksPerWorkspace,
    int MaxQRCodesPerWorkspace,
    int MaxDomainsPerWorkspace,
    int MaxEventsPerMonth,
    bool HasCustomDomains,
    bool HasPasswordProtection,
    bool HasAnalytics
);

public record UsageStats(
    int TotalWorkspaces,
    int TotalLinks,
    int TotalQRCodes,
    int TotalDomains,
    int EventsThisMonth,
    WorkspacePlan HighestPlan
);

public record WorkspaceUsageStats(
    Guid WorkspaceId,
    WorkspacePlan Plan,
    int Links,
    int QRCodes,
    int Domains,
    int EventsThisMonth,
    PlanLimits Limits
);

public class PlanLimitsService(IServiceScopeFactory scopeFactory) : IPlanLimitsService
{
    private static readonly Dictionary<WorkspacePlan, PlanLimits> PlanConfigs = new()
    {
        [WorkspacePlan.Free] = new PlanLimits(
            1,
            50,
            25,
            0,
            10_000,
            false,
            false,
            true
        ),
        [WorkspacePlan.Pro] = new PlanLimits(
            5,
            5_000,
            1_000,
            3,
            100_000,
            true,
            true,
            true
        ),
        [WorkspacePlan.Business] = new PlanLimits(
            int.MaxValue,
            int.MaxValue,
            int.MaxValue,
            int.MaxValue,
            int.MaxValue,
            true,
            true,
            true
        )
    };

    public PlanLimits GetLimits(WorkspacePlan plan)
    {
        return PlanConfigs[plan];
    }

    public async Task<UsageStats> GetUsageAsync(Guid userId, CancellationToken ct = default)
    {
        using var scope = scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        var workspaces = await db.Workspaces
            .Where(w => w.OwnerUserId == userId)
            .Select(w => new { w.Id, w.Plan })
            .ToListAsync(ct);

        var workspaceIds = workspaces.Select(w => w.Id).ToList();

        var totalLinks = await db.ShortLinks
            .CountAsync(l => workspaceIds.Contains(l.WorkspaceId), ct);

        var totalQRCodes = await db.QrCodeDesigns
            .CountAsync(q => workspaceIds.Contains(q.WorkspaceId), ct);

        var totalDomains = await db.Domains
            .CountAsync(d => workspaceIds.Contains(d.WorkspaceId), ct);

        var monthStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1, 0, 0, 0, DateTimeKind.Utc);
        var eventsThisMonth = await db.Events
            .CountAsync(e => workspaceIds.Contains(e.WorkspaceId) && e.Timestamp >= monthStart, ct);

        var highestPlan = workspaces.Any()
            ? workspaces.Max(w => w.Plan)
            : WorkspacePlan.Free;

        return new UsageStats(
            workspaces.Count,
            totalLinks,
            totalQRCodes,
            totalDomains,
            eventsThisMonth,
            highestPlan
        );
    }

    public async Task<WorkspaceUsageStats> GetWorkspaceUsageAsync(Guid workspaceId, CancellationToken ct = default)
    {
        using var scope = scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        var workspace = await db.Workspaces
            .Where(w => w.Id == workspaceId)
            .Select(w => new { w.Id, w.Plan })
            .FirstOrDefaultAsync(ct);

        if (workspace == null)
            throw new KeyNotFoundException("Workspace not found");

        var links = await db.ShortLinks.CountAsync(l => l.WorkspaceId == workspaceId, ct);
        var qrCodes = await db.QrCodeDesigns.CountAsync(q => q.WorkspaceId == workspaceId, ct);
        var domains = await db.Domains.CountAsync(d => d.WorkspaceId == workspaceId, ct);

        var monthStart = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1, 0, 0, 0, DateTimeKind.Utc);
        var eventsThisMonth = await db.Events
            .CountAsync(e => e.WorkspaceId == workspaceId && e.Timestamp >= monthStart, ct);

        var limits = GetLimits(workspace.Plan);

        return new WorkspaceUsageStats(
            workspaceId,
            workspace.Plan,
            links,
            qrCodes,
            domains,
            eventsThisMonth,
            limits
        );
    }

    public async Task<bool> CanCreateWorkspaceAsync(Guid userId, CancellationToken ct = default)
    {
        var usage = await GetUsageAsync(userId, ct);
        var limits = GetLimits(usage.HighestPlan);
        return usage.TotalWorkspaces < limits.MaxWorkspaces;
    }

    public async Task<bool> CanCreateLinkAsync(Guid workspaceId, CancellationToken ct = default)
    {
        var usage = await GetWorkspaceUsageAsync(workspaceId, ct);
        return usage.Links < usage.Limits.MaxLinksPerWorkspace;
    }

    public async Task<bool> CanCreateQRCodeAsync(Guid workspaceId, CancellationToken ct = default)
    {
        var usage = await GetWorkspaceUsageAsync(workspaceId, ct);
        return usage.QRCodes < usage.Limits.MaxQRCodesPerWorkspace;
    }

    public async Task<bool> CanCreateDomainAsync(Guid workspaceId, CancellationToken ct = default)
    {
        var usage = await GetWorkspaceUsageAsync(workspaceId, ct);
        return usage.Domains < usage.Limits.MaxDomainsPerWorkspace;
    }

    public async Task<bool> CanTrackEventAsync(Guid workspaceId, CancellationToken ct = default)
    {
        var usage = await GetWorkspaceUsageAsync(workspaceId, ct);
        return usage.EventsThisMonth < usage.Limits.MaxEventsPerMonth;
    }
}