using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Domain.Entities;
using NexusReader.Infrastructure.Configuration;
using NexusReader.Data.Persistence;
using FluentResults;

namespace NexusReader.Infrastructure.Services;

public class BillingService : IBillingService
{
    private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
    private readonly UserManager<NexusUser> _userManager;
    private readonly StripeSettings _stripeSettings;
    private readonly ILogger<BillingService> _logger;

    public BillingService(
        IDbContextFactory<AppDbContext> dbContextFactory, 
        UserManager<NexusUser> userManager,
        IOptions<StripeSettings> stripeSettings,
        ILogger<BillingService> logger)
    {
        _dbContextFactory = dbContextFactory;
        _userManager = userManager;
        _stripeSettings = stripeSettings.Value;
        _logger = logger;
    }

    public async Task<Result> HandleSubscriptionUpdatedAsync(string customerEmail, string stripeProductId)
    {
        try
        {
            var user = await _userManager.FindByEmailAsync(customerEmail);
            if (user == null) 
            {
                _logger.LogWarning("Attempted to update subscription for non-existent user: {Email}", customerEmail);
                return Result.Fail($"User {customerEmail} not found.");
            }

            string targetPlanName = SubscriptionPlan.FreeName;
            int tokenLimit = 1000;

            if (stripeProductId == _stripeSettings.ProProductId)
            {
                targetPlanName = SubscriptionPlan.ProName;
                tokenLimit = 50000;
            }
            else if (stripeProductId == _stripeSettings.BasicProductId)
            {
                targetPlanName = SubscriptionPlan.BasicName;
                tokenLimit = 10000;
            }
            else if (!string.IsNullOrEmpty(stripeProductId) && stripeProductId != _stripeSettings.FreeProductId)
            {
                 _logger.LogWarning("Unrecognized Stripe Product ID: {ProductId} for user {Email}. Falling back to Free tier.", stripeProductId, customerEmail);
            }

            using var dbContext = await _dbContextFactory.CreateDbContextAsync();
            var plan = await dbContext.SubscriptionPlans.FirstOrDefaultAsync(p => p.PlanName == targetPlanName);
            if (plan != null)
            {
                user.SubscriptionPlanId = plan.Id;
                user.AITokenLimit = tokenLimit;
            }

            var result = await _userManager.UpdateAsync(user);
            if (!result.Succeeded)
            {
                _logger.LogError("Failed to update user {Email} after subscription change: {Errors}", 
                    customerEmail, string.Join(", ", result.Errors.Select(e => e.Description)));
                return Result.Fail(result.Errors.Select(e => e.Description).FirstOrDefault() ?? "Failed to update user profile.");
            }

            return Result.Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error during subscription update for {Email}", customerEmail);
            return Result.Fail(new Error("Unexpected error during subscription update.").CausedBy(ex));
        }
    }

    public async Task<Result> HandleSubscriptionDeletedAsync(string customerEmail)
    {
        try
        {
            var user = await _userManager.FindByEmailAsync(customerEmail);
            if (user == null)
            {
                _logger.LogWarning("Attempted to delete subscription for non-existent user: {Email}", customerEmail);
                return Result.Fail($"User {customerEmail} not found.");
            }

            using var dbContext = await _dbContextFactory.CreateDbContextAsync();
            var freePlan = await dbContext.SubscriptionPlans.FirstOrDefaultAsync(p => p.PlanName == SubscriptionPlan.FreeName);
            if (freePlan != null)
            {
                user.SubscriptionPlanId = freePlan.Id;
                user.AITokenLimit = freePlan.AITokenLimit;
            }
            
            var result = await _userManager.UpdateAsync(user);
            if (!result.Succeeded)
            {
                _logger.LogError("Failed to reset user {Email} to Free tier after subscription deletion: {Errors}", 
                    customerEmail, string.Join(", ", result.Errors.Select(e => e.Description)));
                return Result.Fail(result.Errors.Select(e => e.Description).FirstOrDefault() ?? "Failed to reset user to free tier.");
            }

            return Result.Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error during subscription deletion for {Email}", customerEmail);
            return Result.Fail(new Error("Unexpected error during subscription deletion.").CausedBy(ex));
        }
    }
}
