using System;
using System.Collections.Generic;
using System.Linq;

namespace QuantEngine.Core.Domain;

public record TargetAllocation(
    string Ticker,
    decimal TargetWeightRatio, // 0.0 ~ 1.0
    decimal TargetAmountKrw,
    int TargetQuantity,
    string SizingReason
);

public record PortfolioSizingPacket(
    string AsOfDate,
    decimal TotalCapitalKrw,
    decimal ReservedCashKrw,
    List<TargetAllocation> Allocations,
    bool AllCapsSatisfied,
    string Provenance
);

/// <summary>
/// Volatility Targeting & Risk-Budget Portfolio Sizer
/// SOLID: Single Responsibility for deterministic portfolio weight synthesis and cap enforcement.
/// </summary>
public class PortfolioSizer
{
    private const decimal MaxSingleStockCap = 0.25m; // 25% max per stock
    private const decimal TargetPortfolioVol = 0.12m; // 12% target annualized volatility

    public PortfolioSizingPacket CalculateTargetAllocations(
        string asOfDate,
        decimal totalCapitalKrw,
        decimal cashReserveRatio,
        Dictionary<string, decimal> tickerScores,
        Dictionary<string, decimal> tickerPrices,
        Dictionary<string, decimal> tickerVolatilities)
    {
        if (totalCapitalKrw <= 0 || tickerScores == null || tickerScores.Count == 0)
        {
            return new PortfolioSizingPacket(
                asOfDate,
                totalCapitalKrw,
                totalCapitalKrw,
                new List<TargetAllocation>(),
                true,
                "DATA_INVALID"
            );
        }

        decimal cashAmount = totalCapitalKrw * cashReserveRatio;
        decimal investableCapital = totalCapitalKrw - cashAmount;

        // Filter valid positive scores
        var validScores = tickerScores.Where(kv => kv.Value > 0).ToList();
        if (validScores.Count == 0)
        {
            return new PortfolioSizingPacket(
                asOfDate,
                totalCapitalKrw,
                totalCapitalKrw,
                new List<TargetAllocation>(),
                true,
                "NO_POSITIVE_SCORES"
            );
        }

        decimal sumScores = validScores.Sum(kv => kv.Value);
        var rawAllocations = new List<TargetAllocation>();
        bool capsSatisfied = true;

        foreach (var (ticker, score) in validScores)
        {
            decimal rawWeight = sumScores > 0 ? (score / sumScores) * (1m - cashReserveRatio) : 0m;
            
            // Volatility targeting adjustment if volatility is provided
            if (tickerVolatilities != null && tickerVolatilities.TryGetValue(ticker, out var vol) && vol > 0)
            {
                var volScalar = Math.Min(1.5m, TargetPortfolioVol / vol);
                rawWeight *= volScalar;
            }

            // Cap enforcement (Max 25%)
            decimal finalWeight = rawWeight;
            string reason = "VOL_WEIGHTED";
            if (finalWeight > MaxSingleStockCap)
            {
                finalWeight = MaxSingleStockCap;
                reason = "SINGLE_STOCK_CAP_25%";
                capsSatisfied = true; // Cap correctly enforced
            }

            decimal price = tickerPrices != null && tickerPrices.TryGetValue(ticker, out var p) ? p : 0m;
            decimal targetAmount = investableCapital * (finalWeight / (1m - cashReserveRatio));
            int targetQty = price > 0 ? (int)Math.Floor(targetAmount / price) : 0;

            rawAllocations.Add(new TargetAllocation(
                ticker,
                Math.Round(finalWeight, 4),
                Math.Round(targetAmount, 2),
                targetQty,
                reason
            ));
        }

        return new PortfolioSizingPacket(
            asOfDate,
            totalCapitalKrw,
            Math.Round(cashAmount, 2),
            rawAllocations,
            capsSatisfied,
            $"portfolio_sizer_v1:{asOfDate}"
        );
    }
}
