using MongoDB.Driver;
using MongoDB.Bson;
using System.Text.Json;
using System.Net.Http;
using OnlyOneAccessTemplate.Models;

namespace OnlyOneAccessTemplate.Services
{
    public class ConversionService : IConversionService
    {
        private readonly IMongoDatabase _database;
        private readonly IMongoCollection<ConversionRecord> _conversions;
        private readonly IMongoCollection<ConversionEvent> _events;
        private readonly ISiteConfigurationService _siteConfig;
        private readonly HttpClient _httpClient;
        private readonly ILogger<ConversionService> _logger;

        public ConversionService(
            IMongoDatabase database,
            ISiteConfigurationService siteConfig,
            HttpClient httpClient,
            ILogger<ConversionService> logger)
        {
            _database = database;
            _siteConfig = siteConfig;
            _httpClient = httpClient;
            _logger = logger;
            _conversions = _database.GetCollection<ConversionRecord>("conversions");
            _events = _database.GetCollection<ConversionEvent>("conversion_events");
        }

        public async Task<bool> ProcessConversionAsync(ConversionData data)
        {
            try
            {
                var configuration = await _siteConfig.GetConfigurationAsync(data.Language);

                // Criar registro de conversão
                var conversionRecord = new ConversionRecord
                {
                    Id = ObjectId.GenerateNewId().ToString(),
                    Language = data.Language,
                    PageName = data.PageName,
                    FormData = data.FormData,
                    UserAgent = data.UserAgent,
                    IpAddress = data.IpAddress,
                    Referrer = data.Referrer,
                    ConvertedAt = DateTime.UtcNow,
                    SessionId = GenerateSessionId(),
                    Source = ExtractSource(data.Referrer),
                    Medium = ExtractMedium(data.Referrer),
                    Campaign = ExtractCampaign(data.Referrer)
                };

                await _conversions.InsertOneAsync(conversionRecord);

                // Log evento de conversão
                await LogConversionEventAsync("conversion_completed", new Dictionary<string, object>
            {
                { "conversion_id", conversionRecord.Id },
                { "page_name", data.PageName },
                { "language", data.Language },
                { "source", conversionRecord.Source },
                { "medium", conversionRecord.Medium }
            });

                // Enviar para webhook se configurado
                //if (!string.IsNullOrEmpty(configuration..Conversion.WebhookUrl))
                //{
                //    _ = Task.Run(() => SendWebhookAsync(configuration.Conversion.WebhookUrl, conversionRecord));
                //}

                //// Disparar pixel de conversão se configurado
                //if (!string.IsNullOrEmpty(configuration.Conversion.ConversionPixel))
                //{
                //    _ = Task.Run(() => FireConversionPixelAsync(configuration.Conversion.ConversionPixel, conversionRecord));
                //}

                _logger.LogInformation("Conversão processada com sucesso: {ConversionId}", conversionRecord.Id);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao processar conversão para {Language}/{PageName}", data.Language, data.PageName);
                return false;
            }
        }

        public async Task<ConversionStats> GetConversionStatsAsync(string language, DateTime from, DateTime to)
        {
            try
            {
                var filter = Builders<ConversionRecord>.Filter.And(
                    Builders<ConversionRecord>.Filter.Eq(x => x.Language, language),
                    Builders<ConversionRecord>.Filter.Gte(x => x.ConvertedAt, from),
                    Builders<ConversionRecord>.Filter.Lte(x => x.ConvertedAt, to)
                );

                // Total de conversões
                var totalConversions = await _conversions.CountDocumentsAsync(filter);

                // Conversões por fonte
                var conversionsBySource = await _conversions
                    .Aggregate()
                    .Match(filter)
                    .Group(x => x.Source, g => new { Source = g.Key, Count = g.Count() })
                    .ToListAsync();

                var sourceDict = conversionsBySource.ToDictionary(x => x.Source ?? "direct", x => x.Count);

                // Para calcular taxa de conversão, precisaríamos de dados de visitantes
                // Por enquanto, vamos usar uma estimativa baseada em eventos
                var visitorFilter = Builders<ConversionEvent>.Filter.And(
                    Builders<ConversionEvent>.Filter.Eq("Properties.language", language),
                    Builders<ConversionEvent>.Filter.Eq(x => x.EventName, "page_view"),
                    Builders<ConversionEvent>.Filter.Gte(x => x.Timestamp, from),
                    Builders<ConversionEvent>.Filter.Lte(x => x.Timestamp, to)
                );

                var totalVisits = await _events.CountDocumentsAsync(visitorFilter);
                var conversionRate = totalVisits > 0 ? (double)totalConversions / totalVisits * 100 : 0;

                return new ConversionStats(
                    TotalVisits: (int)totalVisits,
                    TotalConversions: (int)totalConversions,
                    ConversionRate: Math.Round(conversionRate, 2),
                    ConversionsBySource: sourceDict
                );
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao buscar estatísticas de conversão para {Language}", language);
                return new ConversionStats(0, 0, 0, new Dictionary<string, int>());
            }
        }

        public async Task LogConversionEventAsync(string eventName, Dictionary<string, object> properties)
        {
            try
            {
                var conversionEvent = new ConversionEvent
                {
                    Id = ObjectId.GenerateNewId().ToString(),
                    EventName = eventName,
                    Properties = properties,
                    Timestamp = DateTime.UtcNow
                };

                await _events.InsertOneAsync(conversionEvent);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao registrar evento {EventName}", eventName);
            }
        }

        public async Task<ConversionRecord?> GetConversionByIdAsync(string conversionId)
        {
            try
            {
                var filter = Builders<ConversionRecord>.Filter.Eq(x => x.Id, conversionId);
                return await _conversions.Find(filter).FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao buscar conversão {ConversionId}", conversionId);
                return null;
            }
        }

        public async Task<List<ConversionRecord>> GetConversionsAsync(string language, int skip = 0, int take = 50)
        {
            try
            {
                var filter = Builders<ConversionRecord>.Filter.Eq(x => x.Language, language);
                return await _conversions
                    .Find(filter)
                    .SortByDescending(x => x.ConvertedAt)
                    .Skip(skip)
                    .Limit(take)
                    .ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao buscar conversões para idioma {Language}", language);
                return new List<ConversionRecord>();
            }
        }

        public async Task<bool> ValidateFormDataAsync(Dictionary<string, string> formData, List<FormField> formFields)
        {
            try
            {
                foreach (var field in formFields.Where(f => f.Required))
                {
                    if (!formData.ContainsKey(field.Name) || string.IsNullOrWhiteSpace(formData[field.Name]))
                    {
                        return false;
                    }

                    // Validação de email
                    if (field.Type == "email" && !IsValidEmail(formData[field.Name]))
                    {
                        return false;
                    }

                    // Validação de regex personalizada
                    if (!string.IsNullOrEmpty(field.ValidationRegex) &&
                        !System.Text.RegularExpressions.Regex.IsMatch(formData[field.Name], field.ValidationRegex))
                    {
                        return false;
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao validar dados do formulário");
                return false;
            }
        }

        public async Task<ConversionStats> GetRealTimeStatsAsync(string language)
        {
            var now = DateTime.UtcNow;
            var last24Hours = now.AddHours(-24);
            return await GetConversionStatsAsync(language, last24Hours, now);
        }

        public async Task<Dictionary<string, object>> GetConversionMetricsAsync(string language, string period)
        {
            try
            {
                var (from, to) = GetDateRangeFromPeriod(period);
                var stats = await GetConversionStatsAsync(language, from, to);

                var metrics = new Dictionary<string, object>
            {
                { "total_conversions", stats.TotalConversions },
                { "total_visits", stats.TotalVisits },
                { "conversion_rate", stats.ConversionRate },
                { "period", period },
                { "from_date", from },
                { "to_date", to },
                { "conversions_by_source", stats.ConversionsBySource }
            };

                return metrics;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao buscar métricas para {Language}/{Period}", language, period);
                return new Dictionary<string, object>();
            }
        }

        public async Task<bool> SendConversionNotificationAsync(ConversionRecord conversion)
        {
            try
            {
                // Este método pode ser expandido para enviar notificações por email, Slack, etc.
                await LogConversionEventAsync("conversion_notification_sent", new Dictionary<string, object>
            {
                { "conversion_id", conversion.Id },
                { "language", conversion.Language },
                { "page_name", conversion.PageName }
            });

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao enviar notificação de conversão {ConversionId}", conversion.Id);
                return false;
            }
        }

        public async Task<List<ConversionTrend>> GetConversionTrendsAsync(string language, DateTime from, DateTime to, string groupBy = "day")
        {
            try
            {
                var filter = Builders<ConversionRecord>.Filter.And(
                    Builders<ConversionRecord>.Filter.Eq(x => x.Language, language),
                    Builders<ConversionRecord>.Filter.Gte(x => x.ConvertedAt, from),
                    Builders<ConversionRecord>.Filter.Lte(x => x.ConvertedAt, to)
                );

                var conversions = await _conversions.Find(filter).ToListAsync();
                var trends = new List<ConversionTrend>();

                var currentDate = from.Date;
                while (currentDate <= to.Date)
                {
                    var dayConversions = conversions.Where(c => c.ConvertedAt.Date == currentDate).Count();

                    // Para visitas, você pode implementar tracking separado ou usar uma estimativa
                    var dayVisits = dayConversions > 0 ? dayConversions * 10 : 0; // Estimativa
                    var conversionRate = dayVisits > 0 ? (double)dayConversions / dayVisits * 100 : 0;

                    trends.Add(new ConversionTrend(
                        Date: currentDate,
                        Conversions: dayConversions,
                        Visits: dayVisits,
                        ConversionRate: Math.Round(conversionRate, 2)
                    ));

                    currentDate = groupBy switch
                    {
                        "hour" => currentDate.AddHours(1),
                        "week" => currentDate.AddDays(7),
                        "month" => currentDate.AddMonths(1),
                        _ => currentDate.AddDays(1)
                    };
                }

                return trends;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao buscar tendências de conversão para {Language}", language);
                return new List<ConversionTrend>();
            }
        }

        private async Task SendWebhookAsync(string webhookUrl, ConversionRecord conversion)
        {
            try
            {
                var payload = new
                {
                    conversion_id = conversion.Id,
                    language = conversion.Language,
                    page_name = conversion.PageName,
                    form_data = conversion.FormData,
                    converted_at = conversion.ConvertedAt,
                    source = conversion.Source,
                    medium = conversion.Medium,
                    campaign = conversion.Campaign
                };

                var jsonContent = JsonSerializer.Serialize(payload);
                var content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");

                var response = await _httpClient.PostAsync(webhookUrl, content);

                if (response.IsSuccessStatusCode)
                {
                    _logger.LogInformation("Webhook enviado com sucesso para {WebhookUrl}", webhookUrl);
                }
                else
                {
                    _logger.LogWarning("Falha ao enviar webhook para {WebhookUrl}. Status: {StatusCode}",
                        webhookUrl, response.StatusCode);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao enviar webhook para {WebhookUrl}", webhookUrl);
            }
        }

        private async Task FireConversionPixelAsync(string pixelUrl, ConversionRecord conversion)
        {
            try
            {
                // Substituir placeholders no pixel URL
                var processedUrl = pixelUrl
                    .Replace("{conversion_id}", conversion.Id)
                    .Replace("{value}", "1")
                    .Replace("{currency}", "BRL");

                var response = await _httpClient.GetAsync(processedUrl);

                if (response.IsSuccessStatusCode)
                {
                    _logger.LogInformation("Pixel de conversão disparado com sucesso");
                }
                else
                {
                    _logger.LogWarning("Falha ao disparar pixel de conversão. Status: {StatusCode}", response.StatusCode);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao disparar pixel de conversão");
            }
        }

        private string GenerateSessionId()
        {
            return Guid.NewGuid().ToString("N")[..16];
        }

        private string ExtractSource(string? referrer)
        {
            if (string.IsNullOrEmpty(referrer))
                return "direct";

            try
            {
                var uri = new Uri(referrer);
                var host = uri.Host.ToLowerInvariant();

                return host switch
                {
                    var h when h.Contains("google") => "google",
                    var h when h.Contains("facebook") => "facebook",
                    var h when h.Contains("instagram") => "instagram",
                    var h when h.Contains("youtube") => "youtube",
                    var h when h.Contains("linkedin") => "linkedin",
                    var h when h.Contains("twitter") => "twitter",
                    var h when h.Contains("tiktok") => "tiktok",
                    _ => host
                };
            }
            catch
            {
                return "unknown";
            }
        }

        private string ExtractMedium(string? referrer)
        {
            if (string.IsNullOrEmpty(referrer))
                return "direct";

            try
            {
                var uri = new Uri(referrer);
                var query = uri.Query;

                if (query.Contains("utm_medium="))
                {
                    var match = System.Text.RegularExpressions.Regex.Match(query, @"utm_medium=([^&]+)");
                    if (match.Success)
                        return Uri.UnescapeDataString(match.Groups[1].Value);
                }

                var host = uri.Host.ToLowerInvariant();
                return host switch
                {
                    var h when h.Contains("google") => "search",
                    var h when h.Contains("facebook") || h.Contains("instagram") => "social",
                    var h when h.Contains("youtube") || h.Contains("tiktok") => "video",
                    var h when h.Contains("linkedin") || h.Contains("twitter") => "social",
                    _ => "referral"
                };
            }
            catch
            {
                return "unknown";
            }
        }

        private string ExtractCampaign(string? referrer)
        {
            if (string.IsNullOrEmpty(referrer))
                return "direct";

            try
            {
                var uri = new Uri(referrer);
                var query = uri.Query;

                if (query.Contains("utm_campaign="))
                {
                    var match = System.Text.RegularExpressions.Regex.Match(query, @"utm_campaign=([^&]+)");
                    if (match.Success)
                        return Uri.UnescapeDataString(match.Groups[1].Value);
                }

                return "organic";
            }
            catch
            {
                return "unknown";
            }
        }

        private bool IsValidEmail(string email)
        {
            try
            {
                var addr = new System.Net.Mail.MailAddress(email);
                return addr.Address == email;
            }
            catch
            {
                return false;
            }
        }

        private (DateTime from, DateTime to) GetDateRangeFromPeriod(string period)
        {
            var now = DateTime.UtcNow;

            return period.ToLowerInvariant() switch
            {
                "today" => (now.Date, now),
                "yesterday" => (now.Date.AddDays(-1), now.Date.AddSeconds(-1)),
                "last7days" => (now.AddDays(-7), now),
                "last30days" => (now.AddDays(-30), now),
                "thismonth" => (new DateTime(now.Year, now.Month, 1), now),
                "lastmonth" => (new DateTime(now.Year, now.Month, 1).AddMonths(-1),
                               new DateTime(now.Year, now.Month, 1).AddSeconds(-1)),
                _ => (now.AddDays(-7), now) // default to last 7 days
            };
        }
    }

    // Modelos para MongoDB
    public class ConversionRecord
    {
        public string Id { get; set; } = string.Empty;
        public string Language { get; set; } = string.Empty;
        public string PageName { get; set; } = string.Empty;
        public Dictionary<string, string> FormData { get; set; } = new();
        public string UserAgent { get; set; } = string.Empty;
        public string IpAddress { get; set; } = string.Empty;
        public string? Referrer { get; set; }
        public DateTime ConvertedAt { get; set; }
        public string SessionId { get; set; } = string.Empty;
        public string Source { get; set; } = "direct";
        public string Medium { get; set; } = "direct";
        public string Campaign { get; set; } = "direct";
    }

    public class ConversionEvent
    {
        public string Id { get; set; } = string.Empty;
        public string EventName { get; set; } = string.Empty;
        public Dictionary<string, object> Properties { get; set; } = new();
        public DateTime Timestamp { get; set; }
    }
}
