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

// 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.

namespace EonaCat.LogStack.Routing;

/// <summary>
/// Intelligent message router that directs log events to different flows based on pattern matching and rules.
/// This is superior to simple level-based routing as it can apply complex business logic.
/// </summary>
public class IntelligentRouter : IAsyncDisposable
{
    private readonly List<RoutingRule> _rules = new List<RoutingRule>();
    private readonly Dictionary<string, FlowRoute> _flowRoutes = new Dictionary<string, FlowRoute>();
    private readonly object _rulesLock = new object();

    public IntelligentRouter()
    {
    }

    /// <summary>
    /// Adds a routing rule that matches messages based on pattern and routes them to specific flows.
    /// </summary>
    public IntelligentRouter AddRule(RoutingRule rule)
    {
        if (rule == null) throw new ArgumentNullException(nameof(rule));
        lock (_rulesLock)
        {
            _rules.Add(rule);
            // Sort by priority descending so highest priority rules are checked first
            _rules.Sort((a, b) => b.Priority.CompareTo(a.Priority));
        }
        return this;
    }

    /// <summary>
    /// Adds a rule with fluent API support.
    /// </summary>
    public IntelligentRouter AddRule(
        string name,
        Func<LogEvent, bool> matcher,
        IEnumerable<string> targetFlows,
        int priority = 0,
        bool stopIfMatched = false)
    {
        var rule = new RoutingRule
        {
            Name = name,
            Matcher = matcher,
            TargetFlows = targetFlows?.ToList() ?? new List<string>(),
            Priority = priority,
            StopIfMatched = stopIfMatched
        };
        return AddRule(rule);
    }

    /// <summary>
    /// Adds a regex pattern-based routing rule.
    /// </summary>
    public IntelligentRouter AddPatternRule(
        string name,
        string messagePattern,
        IEnumerable<string> targetFlows,
        int priority = 0,
        RegexOptions regexOptions = RegexOptions.IgnoreCase)
    {
        var regex = new Regex(messagePattern, regexOptions);
        return AddRule(
            name,
            logEvent => regex.IsMatch(logEvent.Message.ToString()),
            targetFlows,
            priority
        );
    }

    /// <summary>
    /// Adds a category-based routing rule (routes logs from specific categories).
    /// </summary>
    public IntelligentRouter AddCategoryRule(
        string category,
        IEnumerable<string> targetFlows,
        LogLevel? minimumLevel = null)
    {
        return AddRule(
            $"Category:{category}",
            logEvent => logEvent.Category == category && (minimumLevel == null || logEvent.Level >= minimumLevel),
            targetFlows,
            priority: 10
        );
    }

    /// <summary>
    /// Adds an exception type-based routing rule.
    /// </summary>
    public IntelligentRouter AddExceptionRule(
        string exceptionTypeName,
        IEnumerable<string> targetFlows)
    {
        return AddRule(
            $"Exception:{exceptionTypeName}",
            logEvent => logEvent.Exception?.GetType().Name == exceptionTypeName,
            targetFlows,
            priority: 20,
            stopIfMatched: true
        );
    }

    /// <summary>
    /// Registers a flow that can be targeted by routing rules.
    /// </summary>
    public IntelligentRouter RegisterFlow(string flowName, IFlow flow)
    {
        if (string.IsNullOrEmpty(flowName)) throw new ArgumentNullException(nameof(flowName));
        if (flow == null) throw new ArgumentNullException(nameof(flow));

        lock (_rulesLock)
        {
            _flowRoutes[flowName] = new FlowRoute { Flow = flow, IsEnabled = true };
        }
        return this;
    }

    /// <summary>
    /// Enables or disables a specific flow by name.
    /// </summary>
    public IntelligentRouter SetFlowEnabled(string flowName, bool enabled)
    {
        lock (_rulesLock)
        {
            if (_flowRoutes.TryGetValue(flowName, out var route))
            {
                route.IsEnabled = enabled;
            }
        }
        return this;
    }

    /// <summary>
    /// Routes a log event to appropriate flows based on rules.
    /// Returns true if at least one flow accepted the message.
    /// </summary>
    public async Task<bool> RouteAsync(LogEvent logEvent)
    {
        List<RoutingRule> activeRules;
        Dictionary<string, FlowRoute> activeFlows;

        lock (_rulesLock)
        {
            activeRules = new List<RoutingRule>(_rules);
            activeFlows = new Dictionary<string, FlowRoute>(_flowRoutes);
        }

        var targetFlowNames = new HashSet<string>();
        bool shouldStop = false;

        // Apply rules in priority order
        foreach (var rule in activeRules)
        {
            if (rule.Matcher(logEvent))
            {
                foreach (var flowName in rule.TargetFlows)
                {
                    targetFlowNames.Add(flowName);
                }

                if (rule.StopIfMatched)
                {
                    shouldStop = true;
                    break;
                }
            }
        }

        // Route to target flows
        bool anySuccess = false;
        var tasks = new List<Task>();

        foreach (var flowName in targetFlowNames)
        {
            if (activeFlows.TryGetValue(flowName, out var flowRoute) && flowRoute.IsEnabled)
            {
                tasks.Add(flowRoute.Flow.BlastAsync(logEvent)
                    .ContinueWith(t =>
                    {
                        if (t.Status == TaskStatus.RanToCompletion && t.Result == WriteResult.Success)
                            anySuccess = true;
                    }));
            }
        }

        if (tasks.Any())
        {
            await Task.WhenAll(tasks).ConfigureAwait(false);
        }

        return anySuccess || targetFlowNames.Count == 0; // Return true if no rules matched (default routing)
    }

    /// <summary>
    /// Gets statistics about the router.
    /// </summary>
    public RouterStats GetStats()
    {
        lock (_rulesLock)
        {
            return new RouterStats
            {
                TotalRules = _rules.Count,
                RegisteredFlows = _flowRoutes.Count,
                EnabledFlows = _flowRoutes.Values.Count(f => f.IsEnabled)
            };
        }
    }

    public async ValueTask DisposeAsync()
    {
        lock (_rulesLock)
        {
            _rules.Clear();
            _flowRoutes.Clear();
        }
    }

    private class FlowRoute
    {
        public IFlow Flow { get; set; }
        public bool IsEnabled { get; set; }
    }
}

/// <summary>
/// Defines a routing rule for intelligent message routing.
/// </summary>
public class RoutingRule
{
    /// <summary>
    /// Name of this rule for identification/debugging.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Function that determines if this rule matches a log event.
    /// </summary>
    public Func<LogEvent, bool> Matcher { get; set; }

    /// <summary>
    /// Names of flows this rule routes to.
    /// </summary>
    public List<string> TargetFlows { get; set; } = new List<string>();

    /// <summary>
    /// Priority of this rule (higher priority rules are evaluated first).
    /// </summary>
    public int Priority { get; set; } = 0;

    /// <summary>
    /// If true, stops evaluating further rules after this one matches.
    /// </summary>
    public bool StopIfMatched { get; set; } = false;
}

/// <summary>
/// Statistics about the intelligent router.
/// </summary>
public class RouterStats
{
    public int TotalRules { get; set; }
    public int RegisteredFlows { get; set; }
    public int EnabledFlows { get; set; }
}
