namespace Fast.Shared.Logic.Valuation;

public class WASPPricer
{
    readonly MyDbContext db;
    readonly DateOnly asOfDate;

    public WASPPricer(MyDbContext context, DateOnly? asOfDate)
    {
        db = context;

        if (asOfDate == null)
            this.asOfDate = Util.Date.LastDayOfMonth(DateTime.Today);
        else
            this.asOfDate = Util.Date.LastDayOfMonth(asOfDate.Value);
    }

    private Dictionary<int, double>? brokerCommissions;
    private Dictionary<int, double> BrokerCommissions
    {
        get
        {
            brokerCommissions ??= db.Brokers.ToDictionary(x => x.Id, x => x.Commission.GetValueOrDefault());
            return brokerCommissions;
        }
    }

    private ILookup<string, WaspStructure>? dealsByWaspNum;
    private ILookup<string, WaspStructure> DealsByWaspNum
    {
        get
        {
            dealsByWaspNum ??= (
                    from d in db.Deals
                    join b in db.Brokers on d.BrokerId equals b.Id
                    where d.TransactionTypeId == 3
                        && d.IsPooledFutureDeal == 0
                        && d.TradingDate <= asOfDate
                    select new WaspStructure
                    {
                        TicketNum = d.TicketNum ?? "",
                        WaspNum = d.WaspNum ?? "",
                        Contracts = d.NumOfContracts.GetValueOrDefault(),
                        Price = d.FixedPrice.GetValueOrDefault(),
                        HedgeFee = d.HedgeFee.GetValueOrDefault(),
                        Commission = b.Commission.GetValueOrDefault(),
                        BuyButton = d.BuyButton.GetValueOrDefault()
                    }
                ).ToLookup(x => x.WaspNum);
            return dealsByWaspNum;
        }
    }

    private class WaspStructure
    {
        public string TicketNum { get; set; } = "";
        public string WaspNum { get; set; } = "";
        public int Contracts { get; set; }
        public double Price { get; set; }
        public double HedgeFee { get; set; }
        public double Commission { get; set; }
        public int BuyButton { get; set; }
    }

    public class WASPVolPriceResult
    {
        public double? Price { get; set; }
        public int? Volume { get; set; }
        public double? HedgeFee { get; set; }
    }

    public double GetWaspPrice(string WASPNum, int? productId, string TicketNum, int? NumOfContracts, double? FixedPrice, double? HedgeFee, int? BrokerId, int? BuyButton, DateOnly? AccountingMonth)
    {
        var result = GetWaspVolPrice(WASPNum, productId, TicketNum, NumOfContracts, FixedPrice, HedgeFee, BrokerId, BuyButton, AccountingMonth);
        return result.Price.GetValueOrDefault();
    }

    public WASPVolPriceResult GetWaspVolPrice(string WASPNum, int? ProductId, string TicketNum, int? NumOfContracts, double? FixedPrice, double? HedgeFee, int? BrokerId, int? BuyButton, DateOnly? AccountingMonth)
    {
        var result = new WASPVolPriceResult();
        AccountingMonth = AccountingMonth == null ? DateOnly.FromDateTime(DateTime.Today) : AccountingMonth;

        List<WaspStructure> WASPs = DealsByWaspNum[WASPNum].ToList();

        if (!string.IsNullOrEmpty(TicketNum))
            WASPs = WASPs.Where(x => x.TicketNum != TicketNum).ToList();

        double commission = 0.0;
        if (BrokerId != null)
            commission = BrokerCommissions[BrokerId.Value];

        if (NumOfContracts != null)
        {
            WaspStructure x = new()
            {
                TicketNum = TicketNum,
                WaspNum = WASPNum,
                Contracts = NumOfContracts.GetValueOrDefault(),
                Price = FixedPrice.GetValueOrDefault(),
                HedgeFee = HedgeFee.GetValueOrDefault(),
                Commission = commission.GetHashCode(),
                BuyButton = BuyButton.GetValueOrDefault()
            };
            WASPs.Add(x);
        }

        double volPerContract = 0.0;

        if (ProductId != null)
            volPerContract = Valuater.GetVolumePerContract(ProductId.Value);

        int WASPVol = Math.Abs(WASPs.Sum(x => x.Contracts * x.BuyButton));
        double WASPPrice;
        if (WASPVol > 0)
        {
            double WATP = Math.Abs(WASPs.Sum(x => x.Price * (double)x.Contracts * x.BuyButton) / (double)WASPVol);
            double WAHedgeFee = Math.Abs(WASPs.Sum(x => x.HedgeFee * (double)x.Contracts * x.BuyButton) / (double)WASPVol);
            double WACommission = (WASPs.Sum(x => x.BuyButton == -1 ? x.Commission : 0.0 * x.Contracts) / (double)WASPVol) * 2.0 / volPerContract;
            WASPPrice = WATP - WAHedgeFee - WACommission;
        }
        else
            WASPPrice = FixedPrice.GetValueOrDefault();

        long HedgeFeeMonth;
        double CalculatedHedgeFee;
        double WASPPriceWithHedge;
        HedgeFeeMonth = (AccountingMonth.Value.Year - asOfDate.Year) * 12 + AccountingMonth.Value.Month - asOfDate.Month;

        // for Buy-back BuyButton=1 deals, set hedge fee to 0 to allow client to adjust on their own by price.
        if (BuyButton == 1)
            CalculatedHedgeFee = 0;
        else if (HedgeFeeMonth > 1)
            CalculatedHedgeFee = 0.01 + (HedgeFeeMonth - 1) * 0.005;
        else if (HedgeFeeMonth == 1)
            CalculatedHedgeFee = 0.01;
        else
            CalculatedHedgeFee = 0;

        WASPPriceWithHedge = WASPPrice - CalculatedHedgeFee;
        result.Price = Math.Round(WASPPrice, 10, MidpointRounding.AwayFromZero);
        result.Volume = WASPVol;
        result.HedgeFee = Math.Round(CalculatedHedgeFee, 10, MidpointRounding.AwayFromZero);

        return result;
    }
}
