namespace KArtSell.Modules.ModelOperations.Domain;

/// <summary>
/// VS-07 DOMAIN: Risk Alerts Policy
///
/// Pure business logic (no I/O):
/// - Evaluate thresholds against current metrics
/// - Determine alert status (Initial/Warning/Critical)
/// - Calculate escalation timing
/// - Detect alert resolution
///
/// All decisions: deterministic, time-based, repeatable
/// </summary>

public enum AlertSeverity
{
    Initial,
    Warning,
    Critical,
    Resolved
}

public record AlertThreshold(
    string ThresholdType,
    string ThresholdName,
    decimal ThresholdValue,
    int WarnAtMinutes = 2,
    int CriticalAtMinutes = 5);

public record AlertEvaluationResult(
    bool ThresholdBreached,
    string ThresholdType,
    string ThresholdName,
    decimal CurrentValue,
    decimal Threshold,
    decimal Deviation,
    string Message);

public record AlertStatus(
    Guid AlertId,
    string ThresholdType,
    AlertSeverity Severity,
    DateTime TriggeredAt,
    DateTime? WarnedAt,
    DateTime? CriticalAt,
    int MinutesElapsed,
    string Message);

public record AlertEscalationDecision(
    bool ShouldEscalate,
    AlertSeverity FromSeverity,
    AlertSeverity ToSeverity,
    string Reason);

public record AlertResolutionDecision(
    bool ShouldResolve,
    string ResolutionType, // 'threshold_back_to_safe', 'manual'
    string Reason);

public static class RiskAlertsPolicy
{
    /// <summary>
    /// Evaluate if metric breaches threshold
    /// </summary>
    public static AlertEvaluationResult EvaluateThreshold(
        AlertThreshold threshold,
        decimal currentValue)
    {
        var breached = currentValue > threshold.ThresholdValue;
        var deviation = currentValue - threshold.ThresholdValue;

        var message = breached
            ? $"{threshold.ThresholdName}: {currentValue:N2} exceeds {threshold.ThresholdValue:N2}"
            : $"{threshold.ThresholdName}: {currentValue:N2} within safe limits ({threshold.ThresholdValue:N2})";

        return new AlertEvaluationResult(
            ThresholdBreached: breached,
            ThresholdType: threshold.ThresholdType,
            ThresholdName: threshold.ThresholdName,
            CurrentValue: currentValue,
            Threshold: threshold.ThresholdValue,
            Deviation: Math.Max(0, deviation),
            Message: message);
    }

    /// <summary>
    /// Determine current alert severity (time-based escalation)
    /// </summary>
    public static AlertSeverity DetermineSeverity(
        AlertThreshold threshold,
        DateTime triggeredAt,
        DateTime now)
    {
        var minutesElapsed = (int)(now - triggeredAt).TotalMinutes;

        if (minutesElapsed >= threshold.CriticalAtMinutes)
            return AlertSeverity.Critical;

        if (minutesElapsed >= threshold.WarnAtMinutes)
            return AlertSeverity.Warning;

        return AlertSeverity.Initial;
    }

    /// <summary>
    /// Evaluate whether to escalate alert
    /// </summary>
    public static AlertEscalationDecision EvaluateEscalation(
        AlertThreshold threshold,
        AlertSeverity currentSeverity,
        DateTime triggeredAt,
        DateTime now,
        bool thresholdStillBreached)
    {
        if (!thresholdStillBreached)
            return new AlertEscalationDecision(false, currentSeverity, currentSeverity, "Threshold no longer breached");

        var minutesElapsed = (int)(now - triggeredAt).TotalMinutes;
        var targetSeverity = DetermineSeverity(threshold, triggeredAt, now);

        if (targetSeverity > currentSeverity)
        {
            return new AlertEscalationDecision(
                ShouldEscalate: true,
                FromSeverity: currentSeverity,
                ToSeverity: targetSeverity,
                Reason: targetSeverity == AlertSeverity.Warning
                    ? $"Alert persisting for {minutesElapsed} minutes (warn threshold: {threshold.WarnAtMinutes})"
                    : $"Alert persisting for {minutesElapsed} minutes (critical threshold: {threshold.CriticalAtMinutes})");
        }

        return new AlertEscalationDecision(false, currentSeverity, currentSeverity, "No escalation needed");
    }

    /// <summary>
    /// Evaluate whether to resolve alert
    /// </summary>
    public static AlertResolutionDecision EvaluateResolution(
        AlertThreshold threshold,
        decimal currentValue,
        DateTime triggeredAt,
        DateTime now)
    {
        // Check if threshold back to safe
        if (currentValue <= threshold.ThresholdValue)
        {
            var minutesBreached = (int)(now - triggeredAt).TotalMinutes;
            return new AlertResolutionDecision(
                ShouldResolve: true,
                ResolutionType: "threshold_back_to_safe",
                Reason: $"Metric back to safe level ({currentValue:N2} <= {threshold.ThresholdValue:N2}) after {minutesBreached} minutes");
        }

        return new AlertResolutionDecision(
            ShouldResolve: false,
            ResolutionType: "",
            Reason: "Threshold still breached");
    }

    /// <summary>
    /// Calculate deviation severity (for filtering)
    /// Returns a score 0-10 (0=mild, 10=extreme)
    /// </summary>
    public static int CalculateDeviationSeverity(
        decimal currentValue,
        decimal thresholdValue)
    {
        if (currentValue <= thresholdValue)
            return 0;

        var deviationPercent = ((currentValue - thresholdValue) / thresholdValue) * 100;

        return (int)Math.Min(10, Math.Ceiling(deviationPercent / 10));
    }

    /// <summary>
    /// Detect concentration-based alerts
    /// </summary>
    public static bool IsConcentrationAlert(
        List<WeightBreakdown> weights,
        decimal maxSinglePosition = 40,
        decimal maxTopFivePercent = 60)
    {
        if (!weights.Any())
            return false;

        var maxPosition = weights.First().WeightPercent;
        var topFive = weights.Take(5).Sum(w => w.WeightPercent);

        return maxPosition > maxSinglePosition || topFive > maxTopFivePercent;
    }

    /// <summary>
    /// Detect volatility-based alerts
    /// </summary>
    public static bool IsVolatilityAlert(
        decimal annualizedVolatility,
        decimal volatilityThreshold = 0.30m)
    {
        return annualizedVolatility > volatilityThreshold;
    }

    /// <summary>
    /// Detect VAR-based alerts
    /// </summary>
    public static bool IsVARAlert(
        decimal varAmount,
        decimal portfolioValue,
        decimal varThreshold = 0.20m)
    {
        var varPercent = varAmount / portfolioValue;
        return varPercent > varThreshold;
    }

    /// <summary>
    /// Validate alert threshold configuration
    /// </summary>
    public static (bool IsValid, List<string> Issues) ValidateThreshold(AlertThreshold threshold)
    {
        var issues = new List<string>();

        if (threshold.ThresholdValue < 0)
            issues.Add($"Threshold value must be non-negative (got {threshold.ThresholdValue})");

        if (threshold.WarnAtMinutes < 0 || threshold.WarnAtMinutes > 60)
            issues.Add($"Warn timing must be 0-60 minutes (got {threshold.WarnAtMinutes})");

        if (threshold.CriticalAtMinutes <= threshold.WarnAtMinutes)
            issues.Add($"Critical timing must be > warn timing ({threshold.CriticalAtMinutes} must be > {threshold.WarnAtMinutes})");

        if (string.IsNullOrWhiteSpace(threshold.ThresholdType))
            issues.Add("Threshold type required");

        return (issues.Count == 0, issues);
    }

    /// <summary>
    /// Generate alert message (human-readable)
    /// </summary>
    public static string GenerateAlertMessage(
        AlertThreshold threshold,
        decimal currentValue,
        AlertSeverity severity,
        int minutesElapsed)
    {
        var deviation = currentValue - threshold.ThresholdValue;
        var severityLabel = severity switch
        {
            AlertSeverity.Initial => "⚠️",
            AlertSeverity.Warning => "⚠️⚠️",
            AlertSeverity.Critical => "🚨",
            _ => ""
        };

        return $"{severityLabel} {threshold.ThresholdName}: {currentValue:N2} " +
               $"(threshold: {threshold.ThresholdValue:N2}, deviation: +{deviation:N2}) " +
               $"[{minutesElapsed}min]";
    }

    /// <summary>
    /// Determine alert priority (for sorting/notification)
    /// </summary>
    public static int CalculateAlertPriority(
        AlertSeverity severity,
        decimal deviationPercent)
    {
        var severityScore = severity switch
        {
            AlertSeverity.Critical => 300,
            AlertSeverity.Warning => 200,
            AlertSeverity.Initial => 100,
            _ => 0
        };

        var deviationScore = (int)(deviationPercent * 10);

        return severityScore + deviationScore;
    }

    /// <summary>
    /// Batch evaluate all thresholds (for background job)
    /// </summary>
    public static List<AlertEvaluationResult> EvaluateAllThresholds(
        List<AlertThreshold> thresholds,
        Dictionary<string, decimal> currentMetrics)
    {
        return thresholds
            .Select(t =>
            {
                if (currentMetrics.TryGetValue(t.ThresholdType, out var value))
                    return EvaluateThreshold(t, value);

                return new AlertEvaluationResult(
                    ThresholdBreached: false,
                    ThresholdType: t.ThresholdType,
                    ThresholdName: t.ThresholdName,
                    CurrentValue: 0,
                    Threshold: t.ThresholdValue,
                    Deviation: 0,
                    Message: "Metric not available");
            })
            .ToList();
    }
}
