using PlotLine.Data;
using PlotLine.Models;

namespace PlotLine.Services;

public interface ISubscriptionService
{
    Task<UserSubscriptionDetail?> GetCurrentSubscriptionAsync(int userId);
    Task<SubscriptionLevel?> GetSubscriptionLevelAsync(int subscriptionLevelId);
    Task<IReadOnlyList<SubscriptionLevel>> GetActiveSubscriptionLevelsAsync();
    Task<SubscriptionLimitResult> CanCreateBookAsync(int userId);
    Task<SubscriptionLimitResult> CanCreateChapterAsync(int bookId, int userId);
    Task<SubscriptionLimitResult> CanCreateSceneAsync(int bookId, int userId);
    Task<SubscriptionLimitResult> CanCreateCharacterAsync(int projectId, int userId);
    Task<SubscriptionLimitResult> CanInviteCollaboratorAsync(int userId);
}

public sealed class SubscriptionService(ISubscriptionRepository subscriptions) : ISubscriptionService
{
    private const int PracticalUnlimited = 999999;

    public Task<UserSubscriptionDetail?> GetCurrentSubscriptionAsync(int userId) =>
        subscriptions.GetCurrentSubscriptionAsync(userId);

    public Task<SubscriptionLevel?> GetSubscriptionLevelAsync(int subscriptionLevelId) =>
        subscriptions.GetSubscriptionLevelAsync(subscriptionLevelId);

    public Task<IReadOnlyList<SubscriptionLevel>> GetActiveSubscriptionLevelsAsync() =>
        subscriptions.GetActiveSubscriptionLevelsAsync();

    public async Task<SubscriptionLimitResult> CanCreateBookAsync(int userId)
    {
        var subscription = await GetRequiredSubscriptionAsync(userId);
        var currentUsage = await subscriptions.GetOwnedBookCountAsync(userId);
        return Evaluate(subscription, currentUsage, subscription.MaxBooks, "books");
    }

    public async Task<SubscriptionLimitResult> CanCreateChapterAsync(int bookId, int userId)
    {
        var subscription = await GetRequiredSubscriptionAsync(userId);
        var currentUsage = await subscriptions.GetChapterCountForBookAsync(bookId);
        return Evaluate(subscription, currentUsage, subscription.MaxChaptersPerBook, "chapters per book");
    }

    public async Task<SubscriptionLimitResult> CanCreateSceneAsync(int bookId, int userId)
    {
        var subscription = await GetRequiredSubscriptionAsync(userId);
        var currentUsage = await subscriptions.GetSceneCountForBookAsync(bookId);
        return Evaluate(subscription, currentUsage, subscription.MaxScenesPerBook, "scenes per book");
    }

    public async Task<SubscriptionLimitResult> CanCreateCharacterAsync(int projectId, int userId)
    {
        var subscription = await GetRequiredSubscriptionAsync(userId);
        var currentUsage = await subscriptions.GetCharacterCountForProjectAsync(projectId);
        return Evaluate(subscription, currentUsage, subscription.MaxCharactersPerProject, "characters per project");
    }

    public async Task<SubscriptionLimitResult> CanInviteCollaboratorAsync(int userId)
    {
        var subscription = await GetRequiredSubscriptionAsync(userId);
        var currentUsage = await subscriptions.GetCollaboratorCountForOwnerAsync(userId);
        return Evaluate(subscription, currentUsage, subscription.MaxCollaborators, "collaborators");
    }

    private async Task<UserSubscriptionDetail> GetRequiredSubscriptionAsync(int userId)
    {
        var subscription = await subscriptions.GetCurrentSubscriptionAsync(userId);
        if (subscription is null)
        {
            throw new SubscriptionLimitException("No active subscription is assigned to your account.");
        }

        return subscription;
    }

    private static SubscriptionLimitResult Evaluate(UserSubscriptionDetail subscription, int currentUsage, int maximumAllowed, string limitName)
    {
        if (maximumAllowed >= PracticalUnlimited || currentUsage < maximumAllowed)
        {
            return SubscriptionLimitResult.AllowedResult(currentUsage, maximumAllowed);
        }

        return SubscriptionLimitResult.Blocked(
            currentUsage,
            maximumAllowed,
            $"You have reached the maximum number of {limitName} allowed by your {subscription.DisplayName} subscription ({maximumAllowed:N0}). Upgrade your subscription to add more.");
    }
}

public sealed class SubscriptionLimitException(string message) : InvalidOperationException(message);
