using System;
using System.Collections.Generic;

namespace NT8.Core.Intelligence
{
    /// <summary>
    /// Types of confluence factors used in intelligence scoring.
    /// </summary>
    public enum FactorType
    {
        /// <summary>
        /// Base setup quality (for example ORB validity).
        /// </summary>
        Setup = 0,

        /// <summary>
        /// Trend alignment quality.
        /// </summary>
        Trend = 1,

        /// <summary>
        /// Volatility regime suitability.
        /// </summary>
        Volatility = 2,

        /// <summary>
        /// Session and timing quality.
        /// </summary>
        Timing = 3,

        /// <summary>
        /// Execution quality and slippage context.
        /// </summary>
        ExecutionQuality = 4,

        /// <summary>
        /// Liquidity and microstructure quality.
        /// </summary>
        Liquidity = 5,

        /// <summary>
        /// Risk regime and portfolio context.
        /// </summary>
        Risk = 6,

        /// <summary>
        /// Additional custom factor.
        /// </summary>
        Custom = 99
    }

    /// <summary>
    /// Trade grade produced from weighted confluence score.
    /// </summary>
    public enum TradeGrade
    {
        /// <summary>
        /// Exceptional setup, score 0.90 and above.
        /// </summary>
        APlus = 6,

        /// <summary>
        /// Strong setup, score 0.80 and above.
        /// </summary>
        A = 5,

        /// <summary>
        /// Good setup, score 0.70 and above.
        /// </summary>
        B = 4,

        /// <summary>
        /// Acceptable setup, score 0.60 and above.
        /// </summary>
        C = 3,

        /// <summary>
        /// Marginal setup, score 0.50 and above.
        /// </summary>
        D = 2,

        /// <summary>
        /// Reject setup, score below 0.50.
        /// </summary>
        F = 1
    }

    /// <summary>
    /// Weight configuration for a factor type.
    /// </summary>
    public class FactorWeight
    {
        /// <summary>
        /// Factor type this weight applies to.
        /// </summary>
        public FactorType Type { get; set; }

        /// <summary>
        /// Weight value (must be positive).
        /// </summary>
        public double Weight { get; set; }

        /// <summary>
        /// Human-readable reason for this weighting.
        /// </summary>
        public string Reason { get; set; }

        /// <summary>
        /// Last update timestamp in UTC.
        /// </summary>
        public DateTime UpdatedAtUtc { get; set; }

        /// <summary>
        /// Creates a new factor weight.
        /// </summary>
        /// <param name="type">Factor type.</param>
        /// <param name="weight">Weight value greater than zero.</param>
        /// <param name="reason">Reason for this weight.</param>
        public FactorWeight(FactorType type, double weight, string reason)
        {
            if (weight <= 0)
                throw new ArgumentException("Weight must be greater than zero", "weight");

            Type = type;
            Weight = weight;
            Reason = reason ?? string.Empty;
            UpdatedAtUtc = DateTime.UtcNow;
        }
    }

    /// <summary>
    /// Represents one contributing factor in a confluence calculation.
    /// </summary>
    public class ConfluenceFactor
    {
        /// <summary>
        /// Factor category.
        /// </summary>
        public FactorType Type { get; set; }

        /// <summary>
        /// Factor display name.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Raw factor score in range [0.0, 1.0].
        /// </summary>
        public double Score { get; set; }

        /// <summary>
        /// Weight importance for this factor.
        /// </summary>
        public double Weight { get; set; }

        /// <summary>
        /// Explanation for score value.
        /// </summary>
        public string Reason { get; set; }

        /// <summary>
        /// Additional details for diagnostics.
        /// </summary>
        public Dictionary<string, object> Details { get; set; }

        /// <summary>
        /// Creates a new confluence factor.
        /// </summary>
        /// <param name="type">Factor type.</param>
        /// <param name="name">Factor name.</param>
        /// <param name="score">Score in range [0.0, 1.0].</param>
        /// <param name="weight">Weight greater than zero.</param>
        /// <param name="reason">Reason for the score.</param>
        /// <param name="details">Extended details dictionary.</param>
        public ConfluenceFactor(
            FactorType type,
            string name,
            double score,
            double weight,
            string reason,
            Dictionary<string, object> details)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");
            if (score < 0.0 || score > 1.0)
                throw new ArgumentException("Score must be between 0.0 and 1.0", "score");
            if (weight <= 0.0)
                throw new ArgumentException("Weight must be greater than zero", "weight");

            Type = type;
            Name = name;
            Score = score;
            Weight = weight;
            Reason = reason ?? string.Empty;
            Details = details ?? new Dictionary<string, object>();
        }
    }

    /// <summary>
    /// Represents an overall confluence score and grade for a trading decision.
    /// </summary>
    public class ConfluenceScore
    {
        /// <summary>
        /// Unweighted aggregate score in range [0.0, 1.0].
        /// </summary>
        public double RawScore { get; set; }

        /// <summary>
        /// Weighted aggregate score in range [0.0, 1.0].
        /// </summary>
        public double WeightedScore { get; set; }

        /// <summary>
        /// Grade derived from weighted score.
        /// </summary>
        public TradeGrade Grade { get; set; }

        /// <summary>
        /// Factor breakdown used to produce the score.
        /// </summary>
        public List<ConfluenceFactor> Factors { get; set; }

        /// <summary>
        /// Calculation timestamp in UTC.
        /// </summary>
        public DateTime CalculatedAt { get; set; }

        /// <summary>
        /// Additional metadata and diagnostics.
        /// </summary>
        public Dictionary<string, object> Metadata { get; set; }

        /// <summary>
        /// Creates a new confluence score model.
        /// </summary>
        /// <param name="rawScore">Unweighted score in [0.0, 1.0].</param>
        /// <param name="weightedScore">Weighted score in [0.0, 1.0].</param>
        /// <param name="grade">Trade grade.</param>
        /// <param name="factors">Contributing factors.</param>
        /// <param name="calculatedAt">Calculation timestamp.</param>
        /// <param name="metadata">Additional metadata.</param>
        public ConfluenceScore(
            double rawScore,
            double weightedScore,
            TradeGrade grade,
            List<ConfluenceFactor> factors,
            DateTime calculatedAt,
            Dictionary<string, object> metadata)
        {
            if (rawScore < 0.0 || rawScore > 1.0)
                throw new ArgumentException("RawScore must be between 0.0 and 1.0", "rawScore");
            if (weightedScore < 0.0 || weightedScore > 1.0)
                throw new ArgumentException("WeightedScore must be between 0.0 and 1.0", "weightedScore");

            RawScore = rawScore;
            WeightedScore = weightedScore;
            Grade = grade;
            Factors = factors ?? new List<ConfluenceFactor>();
            CalculatedAt = calculatedAt;
            Metadata = metadata ?? new Dictionary<string, object>();
        }
    }
}
