using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EonaCat.LogStack.Core;

namespace EonaCat.LogStack.Features
{
    /// <summary>
    /// Rule-based alerting engine for proactive log monitoring and notifications.
    /// Monitors log streams and triggers alerts based on configurable conditions.
    /// </summary>
    public class AlertingEngine
    {
        private readonly List<AlertRule> _rules = new();
        private readonly object _lockObject = new();
        private bool _isEnabled = true;

        /// <summary>
        /// Event fired when an alert is triggered.
        /// </summary>
        public event EventHandler<AlertTriggeredEventArgs> AlertTriggered;

        /// <summary>
        /// Enables or disables the alerting engine.
        /// </summary>
        public void SetEnabled(bool enabled)
        {
            _isEnabled = enabled;
        }

        /// <summary>
        /// Adds a new alert rule.
        /// </summary>
        public AlertRule AddRule(string ruleName, Action<AlertRuleBuilder> configureAction)
        {
            if (string.IsNullOrWhiteSpace(ruleName))
                throw new ArgumentException("Rule name cannot be empty", nameof(ruleName));

            var builder = new AlertRuleBuilder(ruleName);
            configureAction(builder);
            var rule = builder.Build();

            lock (_lockObject)
            {
                _rules.Add(rule);
            }

            return rule;
        }

        /// <summary>
        /// Removes a rule by name.
        /// </summary>
        public bool RemoveRule(string ruleName)
        {
            lock (_lockObject)
            {
                var rule = _rules.FirstOrDefault(r => r.Name.Equals(ruleName, StringComparison.OrdinalIgnoreCase));
                if (rule != null)
                {
                    _rules.Remove(rule);
                    return true;
                }
                return false;
            }
        }

        /// <summary>
        /// Evaluates a log event against all active rules.
        /// </summary>
        public void EvaluateLog(LogEvent logEvent, string formattedMessage)
        {
            if (!_isEnabled)
                return;

            lock (_lockObject)
            {
                foreach (var rule in _rules.Where(r => r.IsEnabled))
                {
                    if (EvaluateRule(rule, logEvent, formattedMessage))
                    {
                        var alert = new Alert
                        {
                            RuleName = rule.Name,
                            RuleSeverity = rule.Severity,
                            TriggeredAt = DateTime.UtcNow,
                            LogLevel = logEvent.Level,
                            LoggerName = logEvent.Category ?? "Default",
                            Message = formattedMessage,
                            LogEvent = logEvent
                        };

                        AlertTriggered?.Invoke(this, new AlertTriggeredEventArgs { Alert = alert });

                        // Execute async action if configured
                        if (rule.OnAlertAsync != null)
                        {
                            _ = Task.Run(async () => await rule.OnAlertAsync(alert));
                        }
                    }
                }
            }
        }

        private bool EvaluateRule(AlertRule rule, LogEvent logEvent, string formattedMessage)
        {
            // Level check
            if (rule.MinimumLevel.HasValue && logEvent.Level < rule.MinimumLevel.Value)
                return false;

            // Keyword check
            if (!string.IsNullOrEmpty(rule.KeywordPattern))
            {
                if (!formattedMessage.Contains(rule.KeywordPattern, StringComparison.OrdinalIgnoreCase))
                    return false;
            }

            // Logger check
            if (!string.IsNullOrEmpty(rule.LoggerNamePattern))
            {
                if (!(logEvent.Category?? "").Contains(rule.LoggerNamePattern, StringComparison.OrdinalIgnoreCase))
                    return false;
            }

            // Exception check
            if (rule.OnlyWithExceptions && logEvent.Exception == null)
                return false;

            // Custom predicate
            if (rule.CustomPredicate != null)
            {
                if (!rule.CustomPredicate(logEvent, formattedMessage))
                    return false;
            }

            // Check throttling
            if (rule.ThrottleIntervalSeconds > 0)
            {
                var now = DateTime.UtcNow;
                if (rule.LastTriggeredAt.HasValue)
                {
                    var timeSinceLastTrigger = (now - rule.LastTriggeredAt.Value).TotalSeconds;
                    if (timeSinceLastTrigger < rule.ThrottleIntervalSeconds)
                        return false;
                }
                rule.LastTriggeredAt = now;
            }

            return true;
        }

        /// <summary>
        /// Gets all active rules.
        /// </summary>
        public IEnumerable<AlertRule> GetRules()
        {
            lock (_lockObject)
            {
                return _rules.ToList();
            }
        }

        /// <summary>
        /// Gets alerting statistics.
        /// </summary>
        public AlertingStatistics GetStatistics()
        {
            lock (_lockObject)
            {
                return new AlertingStatistics
                {
                    TotalRules = _rules.Count,
                    EnabledRules = _rules.Count(r => r.IsEnabled),
                    Rules = _rules.Select(r => new AlertRuleStats
                    {
                        Name = r.Name,
                        IsEnabled = r.IsEnabled,
                        TimesTriggered = r.TimesTriggered,
                        LastTriggered = r.LastTriggeredAt
                    }).ToList()
                };
            }
        }

        /// <summary>
        /// Clears all alert statistics.
        /// </summary>
        public void ClearStatistics()
        {
            lock (_lockObject)
            {
                foreach (var rule in _rules)
                {
                    rule.TimesTriggered = 0;
                    rule.LastTriggeredAt = null;
                }
            }
        }
    }

    /// <summary>
    /// Represents an alert rule.
    /// </summary>
    public class AlertRule
    {
        public string Name { get; set; }
        public bool IsEnabled { get; set; } = true;
        public AlertSeverity Severity { get; set; } = AlertSeverity.Warning;
        public LogLevel? MinimumLevel { get; set; }
        public string KeywordPattern { get; set; }
        public string LoggerNamePattern { get; set; }
        public bool OnlyWithExceptions { get; set; }
        public int ThrottleIntervalSeconds { get; set; } = 0; // 0 = no throttling
        public Func<LogEvent, string, bool> CustomPredicate { get; set; }
        public Func<Alert, Task> OnAlertAsync { get; set; }
        public int TimesTriggered { get; set; }
        public DateTime? LastTriggeredAt { get; set; }
    }

    /// <summary>
    /// Fluent builder for alert rules.
    /// </summary>
    public class AlertRuleBuilder
    {
        private readonly AlertRule _rule;

        public AlertRuleBuilder(string ruleName)
        {
            _rule = new AlertRule { Name = ruleName };
        }

        public AlertRuleBuilder WithMinimumLevel(LogLevel level)
        {
            _rule.MinimumLevel = level;
            return this;
        }

        public AlertRuleBuilder WithKeywordPattern(string pattern)
        {
            _rule.KeywordPattern = pattern;
            return this;
        }

        public AlertRuleBuilder WithLoggerNamePattern(string pattern)
        {
            _rule.LoggerNamePattern = pattern;
            return this;
        }

        public AlertRuleBuilder OnlyWhenExceptionPresent()
        {
            _rule.OnlyWithExceptions = true;
            return this;
        }

        public AlertRuleBuilder WithSeverity(AlertSeverity severity)
        {
            _rule.Severity = severity;
            return this;
        }

        public AlertRuleBuilder WithThrottling(int intervalSeconds)
        {
            if (intervalSeconds < 0)
                throw new ArgumentException("Throttle interval must be >= 0", nameof(intervalSeconds));
            _rule.ThrottleIntervalSeconds = intervalSeconds;
            return this;
        }

        public AlertRuleBuilder WithCustomCondition(Func<LogEvent, string, bool> predicate)
        {
            _rule.CustomPredicate = predicate;
            return this;
        }

        public AlertRuleBuilder OnAlert(Func<Alert, Task> handler)
        {
            _rule.OnAlertAsync = handler;
            return this;
        }

        public AlertRuleBuilder Disable()
        {
            _rule.IsEnabled = false;
            return this;
        }

        public AlertRule Build()
        {
            return _rule;
        }
    }

    /// <summary>
    /// Represents a triggered alert.
    /// </summary>
    public class Alert
    {
        public string RuleName { get; set; }
        public AlertSeverity RuleSeverity { get; set; }
        public DateTime TriggeredAt { get; set; }
        public LogLevel LogLevel { get; set; }
        public string LoggerName { get; set; }
        public string Message { get; set; }
        public LogEvent LogEvent { get; set; }
    }

    /// <summary>
    /// Alert severity levels.
    /// </summary>
    public enum AlertSeverity
    {
        Info = 0,
        Warning = 1,
        Critical = 2
    }

    /// <summary>
    /// Event args for alert triggered event.
    /// </summary>
    public class AlertTriggeredEventArgs : EventArgs
    {
        public Alert Alert { get; set; }
    }

    /// <summary>
    /// Statistics about alerting engine.
    /// </summary>
    public class AlertingStatistics
    {
        public int TotalRules { get; set; }
        public int EnabledRules { get; set; }
        public List<AlertRuleStats> Rules { get; set; } = new();
    }

    /// <summary>
    /// Statistics for a single alert rule.
    /// </summary>
    public class AlertRuleStats
    {
        public string Name { get; set; }
        public bool IsEnabled { get; set; }
        public int TimesTriggered { get; set; }
        public DateTime? LastTriggered { get; set; }
    }
}
