namespace KArtSell.Modules.ModelOperations.PortfolioReconciliation;

using System;
using System.Collections.Generic;

/// <summary>
/// Detects discrepancies between approved and executed trades.
/// Flags mismatches for manual review.
/// </summary>
public class MismatchDetector
{
    private const decimal QuantityVarianceThreshold = 0.001m; // 0.1%
    private const decimal PriceVarianceThreshold = 0.02m; // 2%
    private const int SettlementDelayThresholdDays = 2;
    private const int UnconfirmedSettlementThresholdDays = 3;

    /// <summary>
    /// Detects all potential mismatches for a reconciliation event.
    /// </summary>
    public List<Mismatch> DetectMismatches(
        int approvedQuantity,
        int executedQuantity,
        decimal approvedPrice,
        decimal executedPrice,
        DateTime tradeDate,
        DateTime expectedSettlementDate,
        DateTime? actualSettlementDate,
        decimal ledgerCostBasis,
        decimal calculatedCostBasis,
        DateTime now)
    {
        var mismatches = new List<Mismatch>();

        var quantityMismatch = DetectQuantityVariance(approvedQuantity, executedQuantity);
        if (quantityMismatch != null)
            mismatches.Add(quantityMismatch);

        var priceMismatch = DetectPriceVariance(approvedPrice, executedPrice);
        if (priceMismatch != null)
            mismatches.Add(priceMismatch);

        var timingMismatch = DetectSettlementTiming(expectedSettlementDate, actualSettlementDate, now);
        if (timingMismatch != null)
            mismatches.Add(timingMismatch);

        var costBasisMismatch = DetectCostBasisMismatch(ledgerCostBasis, calculatedCostBasis);
        if (costBasisMismatch != null)
            mismatches.Add(costBasisMismatch);

        return mismatches;
    }

    /// <summary>
    /// Detects quantity variance (> 0.1%).
    /// </summary>
    private Mismatch? DetectQuantityVariance(int approvedQuantity, int executedQuantity)
    {
        if (approvedQuantity == 0)
            return null;

        var variance = Math.Abs((decimal)(executedQuantity - approvedQuantity) / approvedQuantity);

        if (variance > QuantityVarianceThreshold)
        {
            return new Mismatch
            {
                Type = MismatchType.QuantityVariance,
                Severity = MismatchSeverity.High,
                Description = $"Quantity variance: approved {approvedQuantity}, executed {executedQuantity} ({variance:P2})",
                ApprovedValue = approvedQuantity,
                ExecutedValue = executedQuantity
            };
        }

        return null;
    }

    /// <summary>
    /// Detects price variance (> 2%).
    /// </summary>
    private Mismatch? DetectPriceVariance(decimal approvedPrice, decimal executedPrice)
    {
        if (approvedPrice == 0)
            return null;

        var variance = Math.Abs((executedPrice - approvedPrice) / approvedPrice);

        if (variance > PriceVarianceThreshold)
        {
            return new Mismatch
            {
                Type = MismatchType.PriceVariance,
                Severity = MismatchSeverity.Medium,
                Description = $"Price variance: approved {approvedPrice:C}, executed {executedPrice:C} ({variance:P2})",
                ApprovedValue = (double)approvedPrice,
                ExecutedValue = (double)executedPrice
            };
        }

        return null;
    }

    /// <summary>
    /// Detects settlement timing issues.
    /// </summary>
    private Mismatch? DetectSettlementTiming(DateTime expectedSettlementDate, DateTime? actualSettlementDate, DateTime now)
    {
        if (!actualSettlementDate.HasValue)
        {
            var daysUnconfirmed = (now - expectedSettlementDate).Days;
            if (daysUnconfirmed > UnconfirmedSettlementThresholdDays)
            {
                return new Mismatch
                {
                    Type = MismatchType.SettlementUnconfirmed,
                    Severity = MismatchSeverity.High,
                    Description = $"Settlement unconfirmed for {daysUnconfirmed} days past expected date",
                    ExpectedValue = expectedSettlementDate,
                    ActualValue = null
                };
            }

            return null;
        }

        var delayDays = (actualSettlementDate.Value - expectedSettlementDate).Days;

        if (delayDays > SettlementDelayThresholdDays)
        {
            return new Mismatch
            {
                Type = MismatchType.SettlementDelay,
                Severity = MismatchSeverity.Low,
                Description = $"Settlement delayed {delayDays} days (expected {expectedSettlementDate:yyyy-MM-dd}, actual {actualSettlementDate:yyyy-MM-dd})",
                ExpectedValue = expectedSettlementDate,
                ActualValue = actualSettlementDate.Value
            };
        }

        return null;
    }

    /// <summary>
    /// Detects cost basis discrepancies (> $0.01).
    /// </summary>
    private Mismatch? DetectCostBasisMismatch(decimal ledgerCostBasis, decimal calculatedCostBasis)
    {
        var delta = Math.Abs(ledgerCostBasis - calculatedCostBasis);

        if (delta > 0.01m)
        {
            return new Mismatch
            {
                Type = MismatchType.CostBasisMismatch,
                Severity = MismatchSeverity.Medium,
                Description = $"Cost basis mismatch: ledger {ledgerCostBasis:C}, calculated {calculatedCostBasis:C} (delta: {delta:C})",
                ApprovedValue = (double)ledgerCostBasis,
                ExecutedValue = (double)calculatedCostBasis
            };
        }

        return null;
    }

    /// <summary>
    /// Determines if a set of mismatches requires escalation.
    /// </summary>
    public bool RequiresEscalation(List<Mismatch> mismatches)
    {
        return mismatches.Exists(m => m.Severity == MismatchSeverity.High);
    }

    /// <summary>
    /// Formats mismatches for logging/alerting.
    /// </summary>
    public string FormatMismatchSummary(List<Mismatch> mismatches)
    {
        if (mismatches.Count == 0)
            return "No mismatches detected";

        var summary = $"Detected {mismatches.Count} mismatch(es):\n";
        foreach (var m in mismatches)
        {
            summary += $"  • [{m.Severity}] {m.Type}: {m.Description}\n";
        }

        return summary;
    }
}

public class Mismatch
{
    public MismatchType Type { get; set; }
    public MismatchSeverity Severity { get; set; }
    public string Description { get; set; } = string.Empty;
    public double? ApprovedValue { get; set; }
    public double? ExecutedValue { get; set; }
    public DateTime? ExpectedValue { get; set; }
    public DateTime? ActualValue { get; set; }
}

public enum MismatchType
{
    QuantityVariance,
    PriceVariance,
    SettlementDelay,
    SettlementUnconfirmed,
    CostBasisMismatch
}

public enum MismatchSeverity
{
    Low,
    Medium,
    High
}
