using MongoDB.Driver;
using MongoDB.Bson;
using QRRapidoApp.Data;
using QRRapidoApp.Models;
using QRRapidoApp.Models.ViewModels;
using System.Text.Json;

namespace QRRapidoApp.Services
{
    public class UserService : IUserService
    {
        private readonly MongoDbContext _context;
        private readonly IConfiguration _config;
        private readonly ILogger<UserService> _logger;

        public UserService(MongoDbContext context, IConfiguration config, ILogger<UserService> logger)
        {
            _context = context;
            _config = config;
            _logger = logger;
        }

        public async Task<User?> GetUserAsync(string userId)
        {
            try
            {
                if (_context.Users == null) return null; // Development mode without MongoDB
                User? userData = null;
                
                if (!String.IsNullOrEmpty(userId))
                {
                    userData = await _context.Users.Find(u => u.Id == userId).FirstOrDefaultAsync();
                }

                return userData ?? new User();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting user {userId}: {ex.Message}");
                return null;
            }
        }

        public async Task<User?> GetUserByEmailAsync(string email)
        {
            try
            {
                if (_context.Users == null) return null; // Development mode without MongoDB
                
                return await _context.Users.Find(u => u.Email == email).FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting user by email {email}: {ex.Message}");
                return null;
            }
        }

        public async Task<User?> GetUserByProviderAsync(string provider, string providerId)
        {
            try
            {
                return await _context.Users
                    .Find(u => u.Provider == provider && u.ProviderId == providerId)
                    .FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting user by provider {provider}:{providerId}: {ex.Message}");
                return null;
            }
        }

        public async Task<User> CreateUserAsync(string email, string name, string provider, string providerId)
        {
            var user = new User
            {
                Email = email,
                Name = name,
                Provider = provider,
                ProviderId = providerId,
                CreatedAt = DateTime.UtcNow,
                LastLoginAt = DateTime.UtcNow,
                PreferredLanguage = "pt-BR",
                DailyQRCount = 0,
                LastQRDate = DateTime.UtcNow.Date,
                TotalQRGenerated = 0
            };

            await _context.Users.InsertOneAsync(user);
            _logger.LogInformation($"Created new user: {email} via {provider}");

            return user;
        }

        public async Task UpdateLastLoginAsync(string userId)
        {
            try
            {
                var update = Builders<User>.Update
                    .Set(u => u.LastLoginAt, DateTime.UtcNow);

                await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error updating last login for user {userId}: {ex.Message}");
            }
        }

        public async Task<bool> UpdateUserAsync(User user)
        {
            try
            {
                var result = await _context.Users.ReplaceOneAsync(u => u.Id == user.Id, user);
                return result.ModifiedCount > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error updating user {user.Id}: {ex.Message}");
                return false;
            }
        }

        public async Task<int> GetDailyQRCountAsync(string? userId)
        {
            if (string.IsNullOrEmpty(userId))
                return 0; // Anonymous users tracked separately

            try
            {
                var user = await GetUserAsync(userId);
                if (user == null) return 0;

                // Reset count if it's a new day
                if (user.LastQRDate.Date < DateTime.UtcNow.Date)
                {
                    user.DailyQRCount = 0;
                    user.LastQRDate = DateTime.UtcNow.Date;
                    await UpdateUserAsync(user);
                }

                return user.DailyQRCount;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting daily QR count for user {userId}: {ex.Message}");
                return 0;
            }
        }

        public async Task<int> IncrementDailyQRCountAsync(string userId)
        {
            try
            {
                var user = await GetUserAsync(userId);
                if (user == null) return 0;

                // Reset count if it's a new day
                if (user.LastQRDate.Date < DateTime.UtcNow.Date)
                {
                    user.DailyQRCount = 1;
                    user.LastQRDate = DateTime.UtcNow.Date;
                }
                else
                {
                    user.DailyQRCount++;
                }

                user.TotalQRGenerated++;
                await UpdateUserAsync(user);

                // Premium and logged users have unlimited QR codes
                return int.MaxValue;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error incrementing daily QR count for user {userId}: {ex.Message}");
                return 0;
            }
        }

        public async Task<int> GetRemainingQRCountAsync(string userId)
        {
            try
            {
                var user = await GetUserAsync(userId);
                if (user == null) return 0;

                // Premium users have unlimited
                if (user.IsPremium) return int.MaxValue;

                // Logged users (non-premium) have unlimited
                return int.MaxValue;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting remaining QR count for user {userId}: {ex.Message}");
                return 0;
            }
        }

        public async Task<bool> CanGenerateQRAsync(string? userId, bool isPremium)
        {
            // Premium users have unlimited QR codes
            if (isPremium) return true;
            
            // Logged users (non-premium) have unlimited QR codes
            if (!string.IsNullOrEmpty(userId)) return true;

            // Anonymous users have 3 QR codes per day
            var dailyCount = await GetDailyQRCountAsync(userId);
            var limit = 3;

            return dailyCount < limit;
        }

        public async Task SaveQRToHistoryAsync(string? userId, QRGenerationResult qrResult, int costInCredits = 0)
        {
            try
            {
                var qrHistory = new QRCodeHistory
                {
                    Id = string.IsNullOrEmpty(qrResult.QRId) ? Guid.NewGuid().ToString() : qrResult.QRId,
                    UserId = userId,
                    Type = qrResult.RequestSettings?.Type ?? "unknown",
                    Content = qrResult.RequestSettings?.Content ?? "",
                    QRCodeBase64 = qrResult.QRCodeBase64,
                    CustomizationSettings = JsonSerializer.Serialize(qrResult.RequestSettings),
                    CreatedAt = DateTime.UtcNow,
                    Language = qrResult.RequestSettings?.Language ?? "pt-BR",
                    Size = qrResult.RequestSettings?.Size ?? 300,
                    GenerationTimeMs = qrResult.GenerationTimeMs,
                    FromCache = qrResult.FromCache,
                    IsActive = true,
                    LastAccessedAt = DateTime.UtcNow,
                    TrackingId = qrResult.TrackingId,
                    IsDynamic = !string.IsNullOrEmpty(qrResult.TrackingId),
                    CostInCredits = costInCredits
                };

                await _context.QRCodeHistory.InsertOneAsync(qrHistory);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error saving QR to history: {ex.Message}");
            }
        }

        public async Task<List<QRCodeHistory>> GetUserQRHistoryAsync(string userId, int limit = 50)
        {
            try
            {
                return await _context.QRCodeHistory
                    .Find(q => q.UserId == userId && q.IsActive)
                    .SortByDescending(q => q.CreatedAt)
                    .Limit(limit)
                    .ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting QR history for user {userId}: {ex.Message}");
                return new List<QRCodeHistory>();
            }
        }

        public async Task<QRCodeHistory?> GetQRDataAsync(string qrId)
        {
            try
            {
                return await _context.QRCodeHistory
                    .Find(q => q.Id == qrId && q.IsActive)
                    .FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting QR data {qrId}: {ex.Message}");
                return null;
            }
        }

        public async Task<bool> DeleteQRFromHistoryAsync(string userId, string qrId)
        {
            try
            {
                // First verify that the QR code belongs to the user
                var qrCode = await _context.QRCodeHistory
                    .Find(q => q.Id == qrId && q.UserId == userId && q.IsActive)
                    .FirstOrDefaultAsync();

                if (qrCode == null)
                {
                    _logger.LogWarning($"QR code not found or doesn't belong to user - QRId: {qrId}, UserId: {userId}");
                    return false;
                }

                // Soft delete: mark as inactive instead of permanently deleting
                var update = Builders<QRCodeHistory>.Update.Set(q => q.IsActive, false);
                var result = await _context.QRCodeHistory.UpdateOneAsync(
                    q => q.Id == qrId && q.UserId == userId, 
                    update);

                return result.ModifiedCount > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error deleting QR from history - QRId: {qrId}, UserId: {userId}: {ex.Message}");
                return false;
            }
        }

        public async Task<int> GetQRCountThisMonthAsync(string userId)
        {
            try
            {
                var startOfMonth = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1);
                var endOfMonth = startOfMonth.AddMonths(1);

                var count = await _context.QRCodeHistory
                    .CountDocumentsAsync(q => q.UserId == userId && 
                                           q.CreatedAt >= startOfMonth && 
                                           q.CreatedAt < endOfMonth);

                return (int)count;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting monthly QR count for user {userId}: {ex.Message}");
                return 0;
            }
        }

        // MÉTODO REMOVIDO: ExtendAdFreeTimeAsync - não é mais necessário

        public async Task<string> GetUserEmailAsync(string userId)
        {
            var user = await GetUserAsync(userId);
            return user?.Email ?? string.Empty;
        }

        public async Task MarkPremiumCancelledAsync(string userId, DateTime cancelledAt)
        {
            try
            {
                if (_context.Users == null) return; // Development mode without MongoDB

                var update = Builders<User>.Update
                    .Set(u => u.IsPremium, false)
                    .Set(u => u.PremiumCancelledAt, cancelledAt)
                    .Set(u => u.PremiumExpiresAt, null);

                await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
                
                _logger.LogInformation($"Marked premium as cancelled for user {userId} at {cancelledAt}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error marking premium cancelled for user {userId}: {ex.Message}");
            }
        }

        public async Task<List<User>> GetUsersForHistoryCleanupAsync(DateTime cutoffDate)
        {
            try
            {
                if (_context.Users == null) return new List<User>(); // Development mode without MongoDB

                return await _context.Users
                    .Find(u => u.PremiumCancelledAt != null && 
                              u.PremiumCancelledAt < cutoffDate &&
                              u.QRHistoryIds.Count > 0)
                    .ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error getting users for history cleanup: {ex.Message}");
                return new List<User>();
            }
        }

        public async Task DeleteUserHistoryAsync(string userId)
        {
            try
            {
                if (_context.Users == null || _context.QRCodeHistory == null) return; // Development mode without MongoDB

                var user = await GetUserAsync(userId);
                if (user?.QRHistoryIds?.Any() == true)
                {
                    // Remover histórico de QR codes
                    await _context.QRCodeHistory.DeleteManyAsync(qr => user.QRHistoryIds.Contains(qr.Id));
                    
                    // Limpar lista de histórico do usuário
                    var update = Builders<User>.Update.Set(u => u.QRHistoryIds, new List<string>());
                    await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
                    
                    _logger.LogInformation($"Deleted history for user {userId}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error deleting history for user {userId}: {ex.Message}");
            }
        }

        public async Task ActivatePremiumStatus(string userId, string stripeSubscriptionId, DateTime expiryDate)
        {
            // Verifica se é uma nova assinatura (não renovação)
            var user = await GetUserAsync(userId);
            var isNewSubscription = user?.StripeSubscriptionId != stripeSubscriptionId;

            var updateBuilder = Builders<User>.Update
                .Set(u => u.IsPremium, true)
                .Set(u => u.StripeSubscriptionId, stripeSubscriptionId)
                .Set(u => u.PremiumExpiresAt, expiryDate)
                .Unset(u => u.PremiumCancelledAt);

            // Se é nova assinatura, atualiza a data de início (para CDC 7 dias)
            if (isNewSubscription)
            {
                updateBuilder = updateBuilder.Set(u => u.SubscriptionStartedAt, DateTime.UtcNow);
            }

            await _context.Users.UpdateOneAsync(u => u.Id == userId, updateBuilder);
            _logger.LogInformation($"Activated premium for user {userId} (new subscription: {isNewSubscription})");
        }

        public async Task DeactivatePremiumStatus(string stripeSubscriptionId)
        {
            var update = Builders<User>.Update
                .Set(u => u.IsPremium, false)
                .Set(u => u.PremiumCancelledAt, DateTime.UtcNow);

            await _context.Users.UpdateOneAsync(u => u.StripeSubscriptionId == stripeSubscriptionId, update);
            _logger.LogInformation($"Deactivated premium for subscription {stripeSubscriptionId}");
        }

        public async Task<User?> GetUserByStripeCustomerIdAsync(string customerId)
        {
            return await _context.Users.Find(u => u.StripeCustomerId == customerId).FirstOrDefaultAsync();
        }

        public async Task UpdateUserStripeCustomerIdAsync(string userId, string stripeCustomerId)
        {
            var update = Builders<User>.Update.Set(u => u.StripeCustomerId, stripeCustomerId);
            await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
        }

        /// <summary>
        /// Get QR code by tracking ID (for analytics)
        /// </summary>
        public async Task<QRCodeHistory?> GetQRByTrackingIdAsync(string trackingId)
        {
            try
            {
                return await _context.QRCodeHistory
                    .Find(q => q.TrackingId == trackingId && q.IsActive)
                    .FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting QR by tracking ID {TrackingId}: {ErrorMessage}", trackingId, ex.Message);
                return null;
            }
        }

        /// <summary>
        /// Increment scan count for QR code analytics
        /// </summary>
        public async Task IncrementQRScanCountAsync(string trackingId)
        {
            try
            {
                var filter = Builders<QRCodeHistory>.Filter.Eq(q => q.TrackingId, trackingId);
                var update = Builders<QRCodeHistory>.Update
                    .Inc(q => q.ScanCount, 1)
                    .Set(q => q.LastAccessedAt, DateTime.UtcNow);

                var result = await _context.QRCodeHistory.UpdateOneAsync(filter, update);

                if (result.ModifiedCount > 0)
                {
                    _logger.LogDebug("QR scan count incremented - TrackingId: {TrackingId}", trackingId);
                }
                else
                {
                    _logger.LogWarning("Failed to increment scan count - TrackingId not found: {TrackingId}", trackingId);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error incrementing scan count for {TrackingId}: {ErrorMessage}", trackingId, ex.Message);
            }
        }

        public async Task<bool> DeductCreditAsync(string userId)
        {
            try
            {
                var update = Builders<User>.Update.Inc(u => u.Credits, -1);
                var result = await _context.Users.UpdateOneAsync(u => u.Id == userId && u.Credits > 0, update);
                return result.ModifiedCount > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error deducting credit for user {userId}");
                return false;
            }
        }

        public async Task<bool> AddCreditsAsync(string userId, int amount)
        {
            try
            {
                var update = Builders<User>.Update.Inc(u => u.Credits, amount);
                var result = await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
                return result.ModifiedCount > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error adding credits for user {userId}");
                return false;
            }
        }

        public async Task<bool> IncrementFreeUsageAsync(string userId)
        {
            try
            {
                // Limite de 5 QRs gratuitos vitalícios/iniciais
                var user = await GetUserAsync(userId);
                if (user == null || user.FreeQRsUsed >= 5) return false;

                var update = Builders<User>.Update.Inc(u => u.FreeQRsUsed, 1);
                await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error incrementing free usage for user {userId}");
                return false;
            }
        }

        public async Task<QRCodeHistory?> FindDuplicateQRAsync(string userId, string contentHash)
        {
            try
            {
                // Verifica se o hash existe na lista do usuário (rápido)
                var user = await GetUserAsync(userId);
                if (user == null || user.HistoryHashes == null || !user.HistoryHashes.Contains(contentHash))
                {
                    return null;
                }

                // Se existe, busca o objeto completo no histórico
                return await _context.QRCodeHistory
                    .Find(q => q.UserId == userId && q.ContentHash == contentHash && q.IsActive)
                    .SortByDescending(q => q.CreatedAt)
                    .FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error finding duplicate QR for user {userId}");
                return null;
            }
        }

        public async Task<bool> CheckAnonymousLimitAsync(string ipAddress, string deviceId)
        {
            try
            {
                // Definição do limite: 1 por dia
                var limit = 1;
                var today = DateTime.UtcNow.Date;
                var tomorrow = today.AddDays(1);

                // Busca QRs gerados hoje por este IP OU DeviceId
                var count = await _context.QRCodeHistory
                    .CountDocumentsAsync(q => 
                        q.UserId == null && // Apenas anônimos
                        q.CreatedAt >= today && 
                        q.CreatedAt < tomorrow &&
                        (q.IpAddress == ipAddress || q.DeviceId == deviceId)
                    );

                return count < limit;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error checking anonymous limit for IP {ipAddress}");
                // Em caso de erro no banco (timeout, etc), bloqueia por segurança ou libera?
                // Vamos liberar para não prejudicar UX em falha técnica momentânea, 
                // mas logamos o erro.
                return true; 
            }
        }

        public async Task RegisterAnonymousUsageAsync(string ipAddress, string deviceId, string qrId)
        {
            try
            {
                var update = Builders<QRCodeHistory>.Update
                    .Set(q => q.IpAddress, ipAddress)
                    .Set(q => q.DeviceId, deviceId);

                await _context.QRCodeHistory.UpdateOneAsync(q => q.Id == qrId, update);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error registering anonymous usage");
            }
        }

        public async Task<(string rawKey, string prefix)> GenerateApiKeyAsync(string userId, string keyName = "Default")
        {
            var rawKey = $"qr_{Guid.NewGuid():N}{Guid.NewGuid():N}";
            var prefix = rawKey.Substring(0, 8);
            var hash = ComputeSha256Hash(rawKey);

            var keyConfig = new ApiKeyConfig
            {
                KeyHash = hash,
                Prefix = prefix,
                Name = keyName,
                CreatedAt = DateTime.UtcNow,
                IsActive = true
            };

            var update = Builders<User>.Update.Push(u => u.ApiKeys, keyConfig);
            await _context.Users.UpdateOneAsync(u => u.Id == userId, update);

            return (rawKey, prefix);
        }

        public async Task<bool> RevokeApiKeyAsync(string userId, string prefix)
        {
            var filter = Builders<User>.Filter.Eq(u => u.Id, userId);
            var update = Builders<User>.Update.Set("apiKeys.$[key].isActive", false);
            var arrayFilters = new List<ArrayFilterDefinition> { 
                new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("key.prefix", prefix)) 
            };

            var result = await _context.Users.UpdateOneAsync(filter, update, new UpdateOptions { ArrayFilters = arrayFilters });
            return result.ModifiedCount > 0;
        }

        public async Task<User?> GetUserByApiKeyAsync(string rawKey)
        {
            var hash = ComputeSha256Hash(rawKey);
            var user = await _context.Users.Find(u => u.ApiKeys.Any(k => k.KeyHash == hash && k.IsActive)).FirstOrDefaultAsync();
            
            if (user != null)
            {
                // Update last used timestamp (fire and forget)
                var update = Builders<User>.Update.Set("apiKeys.$[key].lastUsedAt", DateTime.UtcNow);
                var arrayFilters = new List<ArrayFilterDefinition> { 
                    new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("key.keyHash", hash)) 
                };
                _ = _context.Users.UpdateOneAsync(u => u.Id == user.Id, update, new UpdateOptions { ArrayFilters = arrayFilters });
            }

            return user;
        }

        public async Task ActivateApiSubscriptionAsync(
            string userId,
            string stripeSubscriptionId,
            ApiPlanTier tier,
            DateTime periodEnd,
            string stripeCustomerId)
        {
            try
            {
                var update = Builders<User>.Update
                    .Set(u => u.ApiSubscription.Tier,                 tier)
                    .Set(u => u.ApiSubscription.Status,               "active")
                    .Set(u => u.ApiSubscription.StripeSubscriptionId, stripeSubscriptionId)
                    .Set(u => u.ApiSubscription.StripeCustomerId,     stripeCustomerId)
                    .Set(u => u.ApiSubscription.CurrentPeriodEnd,     periodEnd)
                    .Set(u => u.ApiSubscription.ActivatedAt,          DateTime.UtcNow)
                    .Set(u => u.ApiSubscription.CanceledAt,           (DateTime?)null);

                await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
                _logger.LogInformation("API subscription activated: user={UserId} tier={Tier}", userId, tier);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error activating API subscription for user {UserId}", userId);
            }
        }

        public async Task<User?> GetUserByApiSubscriptionIdAsync(string stripeSubscriptionId)
        {
            try
            {
                return await _context.Users
                    .Find(u => u.ApiSubscription.StripeSubscriptionId == stripeSubscriptionId)
                    .FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error finding user by API subscription {SubId}", stripeSubscriptionId);
                return null;
            }
        }

        public async Task UpdateApiSubscriptionStatusAsync(
            string stripeSubscriptionId,
            string status,
            ApiPlanTier? newTier = null,
            DateTime? periodEnd = null)
        {
            try
            {
                var updateDef = Builders<User>.Update
                    .Set(u => u.ApiSubscription.Status, status);

                if (newTier.HasValue)
                    updateDef = updateDef.Set(u => u.ApiSubscription.Tier, newTier.Value);

                if (periodEnd.HasValue)
                    updateDef = updateDef.Set(u => u.ApiSubscription.CurrentPeriodEnd, periodEnd.Value);

                if (status == "canceled")
                    updateDef = updateDef.Set(u => u.ApiSubscription.CanceledAt, DateTime.UtcNow);

                await _context.Users.UpdateOneAsync(
                    u => u.ApiSubscription.StripeSubscriptionId == stripeSubscriptionId,
                    updateDef);

                _logger.LogInformation("API subscription {SubId} status → {Status}", stripeSubscriptionId, status);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating API subscription status {SubId}", stripeSubscriptionId);
            }
        }

        private string ComputeSha256Hash(string rawData)
        {
            using (var sha256Hash = System.Security.Cryptography.SHA256.Create())
            {
                byte[] bytes = sha256Hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(rawData));
                var builder = new System.Text.StringBuilder();
                for (int i = 0; i < bytes.Length; i++)
                    builder.Append(bytes[i].ToString("x2"));
                return builder.ToString();
            }
        }
    }
}