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

namespace EonaCat.LogStack.Policies;

// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.

/// <summary>
/// Represents a routing rule that determines which flows should handle an event
/// </summary>
public class RoutingRule
{
    public string Name { get; set; } = "";
    public int Priority { get; set; }
    public Func<LogEvent, bool> Condition { get; set; } = _ => true;
    public List<string> TargetFlows { get; set; } = new();
    public bool IsActive { get; set; } = true;
}

/// <summary>
/// Policy engine that manages conditional routing and flow selection
/// </summary>
public sealed class PolicyEngine
{
    private readonly List<RoutingRule> _rules = new();
    private readonly Dictionary<string, IFlow> _flowRegistry = new();
    private readonly object _lock = new object();

    public PolicyEngine() { }

    /// <summary>
    /// Registers a flow by name
    /// </summary>
    public void RegisterFlow(string name, IFlow flow)
    {
        if (string.IsNullOrEmpty(name))
        {
            throw new ArgumentNullException(nameof(name));
        }

        lock (_lock)
        {
            _flowRegistry[name] = flow ?? throw new ArgumentNullException(nameof(flow));
        }
    }

    /// <summary>
    /// Unregisters a flow
    /// </summary>
    public void UnregisterFlow(string name)
    {
        lock (_lock)
        {
            _flowRegistry.Remove(name);
        }
    }

    /// <summary>
    /// Adds a routing rule
    /// </summary>
    public PolicyEngine AddRule(RoutingRule rule)
    {
        if (rule == null)
        {
            throw new ArgumentNullException(nameof(rule));
        }

        lock (_lock)
        {
            _rules.Add(rule);
            _rules.Sort((a, b) => b.Priority.CompareTo(a.Priority));
        }

        return this;
    }

    /// <summary>
    /// Creates and adds a conditional routing rule
    /// </summary>
    public PolicyEngine AddConditionalRule(
        string name,
        Func<LogEvent, bool> condition,
        List<string> targetFlows,
        int priority = 0)
    {
        var rule = new RoutingRule
        {
            Name = name,
            Condition = condition,
            TargetFlows = targetFlows,
            Priority = priority,
            IsActive = true
        };

        return AddRule(rule);
    }

    /// <summary>
    /// Creates a rule that routes errors to specific flows
    /// </summary>
    public PolicyEngine AddErrorRoute(List<string> targetFlows, int priority = 10)
    {
        return AddConditionalRule(
            "ErrorRoute",
            e => e.Level >= LogLevel.Error,
            targetFlows,
            priority);
    }

    /// <summary>
    /// Creates a rule that routes events from a specific category
    /// </summary>
    public PolicyEngine AddCategoryRoute(string category, List<string> targetFlows, int priority = 5)
    {
        return AddConditionalRule(
            $"Category:{category}",
            e => e.Category == category,
            targetFlows,
            priority);
    }

    /// <summary>
    /// Gets the flows that should handle a given event
    /// </summary>
    public List<IFlow> GetTargetFlows(LogEvent logEvent)
    {
        lock (_lock)
        {
            var targetFlows = new List<IFlow>();

            foreach (var rule in _rules)
            {
                if (!rule.IsActive)
                {
                    continue;
                }

                if (rule.Condition(logEvent))
                {
                    foreach (var flowName in rule.TargetFlows)
                    {
                        if (_flowRegistry.TryGetValue(flowName, out var flow))
                        {
                            targetFlows.Add(flow);
                        }
                    }
                    break; // First matching rule wins
                }
            }

            return targetFlows.Count > 0 ? targetFlows : new List<IFlow>(_flowRegistry.Values);
        }
    }

    /// <summary>
    /// Enables/disables a rule by name
    /// </summary>
    public void SetRuleActive(string ruleName, bool active)
    {
        lock (_lock)
        {
            var rule = _rules.FirstOrDefault(r => r.Name == ruleName);
            if (rule != null)
            {
                rule.IsActive = active;
            }
        }
    }

    /// <summary>
    /// Gets all registered flows
    /// </summary>
    public Dictionary<string, IFlow> GetAllFlows()
    {
        lock (_lock)
        {
            return new Dictionary<string, IFlow>(_flowRegistry);
        }
    }

    /// <summary>
    /// Gets all routing rules
    /// </summary>
    public List<RoutingRule> GetRules()
    {
        lock (_lock)
        {
            return new List<RoutingRule>(_rules);
        }
    }

    /// <summary>
    /// Clears all rules
    /// </summary>
    public void ClearRules()
    {
        lock (_lock)
        {
            _rules.Clear();
        }
    }

    /// <summary>
    /// Gets diagnostics about policies
    /// </summary>
    public PolicyDiagnostics GetDiagnostics()
    {
        lock (_lock)
        {
            return new PolicyDiagnostics
            {
                TotalRules = _rules.Count,
                ActiveRules = _rules.Count(r => r.IsActive),
                RegisteredFlows = _flowRegistry.Count
            };
        }
    }
}

/// <summary>
/// Diagnostics about policy engine state
/// </summary>
public class PolicyDiagnostics
{
    public int TotalRules { get; set; }
    public int ActiveRules { get; set; }
    public int RegisteredFlows { get; set; }
}

/// <summary>
/// Quota management for rate limiting across categories
/// </summary>
public sealed class QuotaManager
{
    private class QuotaEntry
    {
        public int AllowedPerSecond { get; set; }
        public DateTime WindowStart { get; set; } = DateTime.UtcNow;
        public int CurrentCount { get; set; }
    }

    private readonly Dictionary<string, QuotaEntry> _quotas = new();
    private readonly object _lock = new object();

    /// <summary>
    /// Sets the quota for a category
    /// </summary>
    public void SetQuota(string category, int eventsPerSecond)
    {
        if (eventsPerSecond <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(eventsPerSecond));
        }

        lock (_lock)
        {
            _quotas[category] = new QuotaEntry { AllowedPerSecond = eventsPerSecond };
        }
    }

    /// <summary>
    /// Checks if an event should be allowed based on quota
    /// </summary>
    public bool IsAllowed(string category)
    {
        lock (_lock)
        {
            if (!_quotas.TryGetValue(category, out var quota))
            {
                return true; // No quota = unlimited
            }

            var now = DateTime.UtcNow;
            if ((now - quota.WindowStart).TotalSeconds >= 1.0)
            {
                // New window
                quota.WindowStart = now;
                quota.CurrentCount = 0;
            }

            if (quota.CurrentCount < quota.AllowedPerSecond)
            {
                quota.CurrentCount++;
                return true;
            }

            return false;
        }
    }

    /// <summary>
    /// Gets usage statistics for all quotas
    /// </summary>
    public Dictionary<string, (int allowed, int used)> GetStatistics()
    {
        lock (_lock)
        {
            return _quotas.ToDictionary(
                kvp => kvp.Key,
                kvp => (kvp.Value.AllowedPerSecond, kvp.Value.CurrentCount));
        }
    }

    /// <summary>
    /// Resets all quotas
    /// </summary>
    public void Reset()
    {
        lock (_lock)
        {
            foreach (var quota in _quotas.Values)
            {
                quota.CurrentCount = 0;
                quota.WindowStart = DateTime.UtcNow;
            }
        }
    }

    /// <summary>
    /// Clears all quota entries
    /// </summary>
    public void Clear()
    {
        lock (_lock)
        {
            _quotas.Clear();
        }
    }
}

/// <summary>
/// Extension methods for policy management
/// </summary>
public static class PolicyExtensions
{
    /// <summary>
    /// Adds a default error flow route
    /// </summary>
    public static PolicyEngine AddDefaultErrorHandling(
        this PolicyEngine engine,
        string errorFlowName)
    {
        return engine.AddErrorRoute(new List<string> { errorFlowName });
    }

    /// <summary>
    /// Creates a policy engine with common patterns
    /// </summary>
    public static PolicyEngine CreateStandardEngine(
        IFlow consoleFlow,
        IFlow fileFlow,
        IFlow? errorFileFlow = null)
    {
        var engine = new PolicyEngine();
        engine.RegisterFlow("console", consoleFlow);
        engine.RegisterFlow("file", fileFlow);

        if (errorFileFlow != null)
        {
            engine.RegisterFlow("error", errorFileFlow);
            engine.AddErrorRoute(new List<string> { "error", "console" }, priority: 10);
        }

        return engine;
    }
}
