using Fast.Shared.Logic.RateControl;
using Fast.Shared.Logic.Valuation;
using Pairs = Fast.Shared.Logic.ValuationCommon;

namespace Fast.Web.Logic.GasControl
{
    public partial class GasTrackExporter : GasTrackExporterBase
    {
        private const string GasTrackDateFormat = "yyyyMMdd";
        private List<GasTrackExportItem> gasExport = new();
        private RateCalculator? rateCalculator;

        private void FillGeiSupply(GasTrackExportItem gei, GasControlMarketSupply nsn)
        {
            int FromMeterID = GetReceiptMeterID(nsn);
            int ToMeterID = GetDeliveryMeterID(nsn);
            GasControlDeal SupplyDeal = LocalDeals[nsn.SupplyDealID.GetValueOrDefault()];
            gei.bBOT_DEAL = SupplyDeal.TicketNum;
            gei.bVENDOR = Entities[SupplyDeal.CounterpartyID.GetValueOrDefault()].GasTrackVNum;
            gei.bTYPE = GetGasTrackType(SupplyDeal.DealPurposeID, SupplyDeal.PhysicalDealTypeID);
            gei.bPIPELINE = GetPipeName(SupplyDeal.PipelineSourceDeliveryID.GetValueOrDefault());
            if (Meters.ContainsKey(nsn.SupplyMeterID.GetValueOrDefault()))
                gei.bPOINT = Meters[nsn.SupplyMeterID.GetValueOrDefault()].HubCode;
            else
                gei.bPOINT = string.Empty;

            gei.bCONTRACT = GetGasTrackContractID(nsn.PipeContractID);
            gei.bTRADER = SupplyDeal.TraderInitials;
            gei.bTRADE_DATE = SupplyDeal.TradingDate?.ToString(GasTrackDateFormat);
            gei.bSTART_DATE = nsn.NomDate.ToString(GasTrackDateFormat);
            gei.bEND_DATE = nsn.NomDate.ToString(GasTrackDateFormat);
            gei.bPRICE = GetContractPrice(Vals[new Pairs.IntegerDatePair(SupplyDeal.DealID, nsn.NomDate)]).ToString("F5");
            gei.bPREMDISC = SupplyDeal.PremiumOrDiscount?.ToString("F5");
            gei.bPRICE_TYPE = GetPriceType(SupplyDeal.FixedPriceButton);
            gei.bINDEX = GetPriceIndexName(SupplyDeal.PriceIndexID);
            double? NomFuelPercent = rateCalculator!.GetFuelPercent(nsn.PipeContractID, nsn.NomDate, FromMeterID, ToMeterID);
            double NomVol = nsn.NominatedVolume.GetValueOrDefault();
            double NomFuelAmt = Math.Round(NomVol / (1d - NomFuelPercent.GetValueOrDefault()) - NomVol, 0);
            double NomReceipt = NomVol + NomFuelAmt;
            gei.bFUEL_RATE = NomFuelPercent?.ToString("F5");
            gei.bVOLUME = NomReceipt.ToString();
            bool NonJurisdictional = Pipes[Zones[Meters[FromMeterID].ZoneID].PipelineID].NonJurisdictional;
            bool IsAgency = Entities[SupplyDeal.CounterpartyID.GetValueOrDefault()].IsAgency;
            gei.bTRANS_RATE = rateCalculator.GetTransRate(nsn.PipeContractID, nsn.NomDate, FromMeterID, ToMeterID, NonJurisdictional, IsAgency).ToString("F5");
            gei.bINV_PRICE = (Util.Nz(gei.bPRICE) + Util.Nz(gei.bPREMDISC)).ToString("F5");
            gei.bINT_ENTITY = GetCounterpartyName(SupplyDeal.InternalEntityID);
            gei.bOWNERSHIP = GetOwnership(nsn.PipeContractID);
        }

        private void FillGeiMarket(GasTrackExportItem gei, GasControlMarketSupply nsn)
        {
            var RealMarketDealNom = nsn;
            ValuationResult RealMarketDealVal = Vals[new Pairs.IntegerDatePair(nsn.MarketDealID.GetValueOrDefault(), nsn.NomDate)];
            var rmdn = RealMarketDealNom;
            var rmdv = RealMarketDealVal;
            GasControlDeal rmd = LocalDeals[rmdn.MarketDealID.GetValueOrDefault()];
            gei.sSELL_DEAL = rmd.TicketNum;
            gei.sGASTRACK = gei.sSELL_DEAL;
            gei.sCUSTOMER = Entities[rmd.CounterpartyID.GetValueOrDefault()].GasTrackCNum;
            gei.sTYPE = GetGasTrackType(rmd.DealPurposeID, rmd.PhysicalDealTypeID);
            gei.sPIPELINE = GetPipeName(rmd.PipelineSourceDeliveryID.GetValueOrDefault());
            gei.sPIPELINE_ID = rmd.PipelineSourceDeliveryID.GetValueOrDefault();
            if (Meters.ContainsKey(rmdn.MarketMeterID.GetValueOrDefault()))
            {
                gei.sPOINT = Meters[rmdn.MarketMeterID.GetValueOrDefault()].HubCode;
            }
            else
            {
                gei.sPOINT = string.Empty;
            }

            gei.sCONTRACT = GetGasTrackContractID(rmdn.PipeContractID);
            gei.sTRADER = rmd.TraderInitials;
            gei.sTRADE_DATE = rmd.TradingDate?.ToString(GasTrackDateFormat);
            gei.sSTART_DATE = rmdn.NomDate.ToString(GasTrackDateFormat);
            gei.sEND_DATE = rmdn.NomDate.ToString(GasTrackDateFormat);
            gei.sVOLUME = rmdn.NominatedVolume.ToString();
            gei.sPRICE = GetContractPrice(rmdv).ToString("F5");
            gei.sPREMDISC = rmd.PremiumOrDiscount?.ToString("F5");
            gei.sPRICE_TYPE = GetPriceType(rmd.FixedPriceButton);
            gei.sINDEX = GetPriceIndexName(rmd.PriceIndexID);
            gei.sINV_PRICE = (Util.Nz(gei.sPRICE) + Util.Nz(gei.sPREMDISC)).ToString("F5");
            gei.sINT_ENTITY = GetCounterpartyName(rmd.InternalEntityID);
        }

        private static void FillGeiTransfers(GasTrackExportItem gei, List<GasTrackTransferValue> GasTrackTransferValues)
        {
            foreach (var gttv in GasTrackTransferValues)
            {
                TransferSetter Setter = new(gei, gttv.TransferNum)
                {
                    TSF = gttv.TicketNum ?? "",
                    TSF_OUT_POINT = gttv.OutPoint ?? "",
                    TSF_IN_PIPE = gttv.InPipe ?? "",
                    TSF_IN_POINT = gttv.InPoint ?? "",
                    TSF_IN_CONTRACT = gttv.InContract ?? "",
                    TSF_OWNERSHIP = gttv.Ownership ?? ""
                };
                if (gttv.InContract is not null)
                {
                    Setter.TSF_FUEL_RATE = gttv.FuelRate?.ToString("F5") ?? "";
                    Setter.TSF_TRANS_RATE = gttv.TransferRate.ToString("F5");
                }

                Setter.SetItemValues();
            }
        }

        private static void ResetGeiSupplyVolForTransfers(GasTrackExportItem gei, List<GasTrackTransferValue> GasTrackTransferValues, int LastMarketVol)
        {
            // For transfer deals we want the real supply deal's volume to be the last real market deal's nominated volume grossed up by the fuel for every pipeline that it goes through
            // This is instead of the actual nominated volume for the supply deal used by normal deals

            if (GasTrackTransferValues.Any())
            {
                int NextNomVol = LastMarketVol;
                int NomReceiptVol;

                // gross up by transfer fuels
                foreach (var gttv in GasTrackTransferValues)
                {
                    NomReceiptVol = GetNomReceiptVol(NextNomVol, gttv.FuelRate);
                    NextNomVol = NomReceiptVol;
                }
                // gross up by real supply fuel
                double? BeginningSupplyFuelPercent = string.IsNullOrEmpty(gei.bFUEL_RATE) ? null : Convert.ToDouble(gei.bFUEL_RATE);
                NomReceiptVol = GetNomReceiptVol(NextNomVol, BeginningSupplyFuelPercent);
                gei.bVOLUME = NomReceiptVol.ToString();
            }
        }

        private static void ResetGeiMarketVolForTransfers(GasTrackExportItem gei, List<GasTrackTransferValue> GasTrackTransferValues, int FirstSupplyVol)
        {
            // For many supplies going to one market through a transfer, we want the real market deal's volume to be the first real supply deal's nominated volume reduced by the fuel for every pipeline that it goes through
            // This is instead of the actual nominated volume for the market deal used by normal deals

            // We also subtract PTR from the first leg

            if (GasTrackTransferValues.Any())
            {
                int NextNomVol = FirstSupplyVol;
                int NomMarketVol;
                double? BeginningSupplyFuelPercent = string.IsNullOrEmpty(gei.bFUEL_RATE) ? null : Convert.ToDouble(gei.bFUEL_RATE);
                NomMarketVol = GetNomMarketVol(NextNomVol, BeginningSupplyFuelPercent);

                // gross up by transfer fuels
                foreach (var gttv in GasTrackTransferValues)
                {
                    NomMarketVol = GetNomMarketVol(NextNomVol, gttv.FuelRate);
                    NextNomVol = NomMarketVol;
                }
                // gross up by real supply fuel

                gei.sVOLUME = NomMarketVol.ToString();
            }
        }

        private static int GetNomMarketVol(int Volume, double? FuelPercent)
        {
            if (FuelPercent.HasValue && FuelPercent.Value > 0d)
            {
                int NomFuelAmt = (int)Math.Round(Math.Round(Volume * FuelPercent.GetValueOrDefault(), 0));
                int NomMarketVol = Volume - NomFuelAmt;
                return NomMarketVol;
            }
            else
            {
                return Volume;
            }
        }

        private static int GetNomReceiptVol(int Volume, double? FuelPercent)
        {
            if (FuelPercent.HasValue && FuelPercent.Value > 0d)
            {
                int NomFuelAmt = (int)Math.Round(Math.Round(Volume / (1d - FuelPercent.GetValueOrDefault()) - Volume, 0));
                int NomReceiptVol = Volume + NomFuelAmt;
                return NomReceiptVol;
            }
            else
            {
                return Volume;
            }
        }

        private void CheckForInvalidItems()
        {
            var Noms = MarketSupplies;

            // Check for Noms that have DealIDs not included in LocalDeals or that have dates that fall outside of their assoicated deal dates
            var MissingSupplies = from n in Noms
                                  where n.SupplyDealID.HasValue && (!LocalDeals.ContainsKey(n.SupplyDealID.GetValueOrDefault()) || !Vals.ContainsKey(new Pairs.IntegerDatePair(n.SupplyDealID.GetValueOrDefault(), n.NomDate)))
                                  select n;
            var MissingMarkets = from n in Noms
                                 where n.MarketDealID.HasValue && (!LocalDeals.ContainsKey(n.MarketDealID.GetValueOrDefault()) || !Vals.ContainsKey(new Pairs.IntegerDatePair(n.MarketDealID.GetValueOrDefault(), n.NomDate)))
                                 select n;
            if (MissingSupplies.Any() || MissingMarkets.Any())
            {
                var ErrorMessage = new StringBuilder();
                ErrorMessage.Append("All of the following nominations are either associated with deals that no longer exist or have dates that fall outside of their associated deal dates.");
                ErrorMessage.Append(Environment.NewLine);
                var db = Main.CreateContext();
                foreach (var item in MissingSupplies)
                {
                    var nom = item;
                    string nomDate = nom.NomDate.ToString(GasTrackDateFormat) ?? "";
                    string pipeline = "";
                    string ticketNum = "";
                    string receiptPoint = nom.SupplyPointID != null ? Points[nom.SupplyPointID.Value].Name! : "Not Found";
                    string deliveryPoint = nom.MarketPointID != null ? Points[nom.MarketPointID.Value].Name! : "Not Found";

                    if (Zones.TryGetValue(Meters[nom.SupplyMeterID.GetValueOrDefault()].ZoneID, out GasControlZone? zone))
                        pipeline = Pipes[zone.PipelineID].Name ?? "";

                    Deal? supplyDeal = (from d in db.Deals where d.Id == nom.SupplyDealID select d).FirstOrDefault();

                    if (supplyDeal is null)
                    {
                        ticketNum = "Deal Not Found";
                        ErrorMessage.Append($"Pipeline: {pipeline}, Nom Date: {nomDate}, Receipt Point: {receiptPoint}, Delivery Point: {deliveryPoint}, Ticket Number: {ticketNum}");
                        ErrorMessage.Append(Environment.NewLine);
                    }
                    else
                    {
                        ticketNum = supplyDeal.TicketNum ?? "";
                        ErrorMessage.Append($"Pipeline: {pipeline}, Nom Date: {nomDate}, Receipt Point: {receiptPoint}, Delivery Point: {deliveryPoint}, Ticket Number: {ticketNum}");
                        ErrorMessage.Append(Environment.NewLine);
                    }
                }

                foreach (var item in MissingMarkets)
                {
                    var nom = item;
                    string nomDate = nom.NomDate.ToString(GasTrackDateFormat) ?? "";
                    string pipeline = "";
                    string ticketNum = "";
                    string receiptPoint = nom.SupplyPointID != null ? Points[nom.SupplyPointID.Value].Name! : "Not Found";
                    string deliveryPoint = nom.MarketPointID != null ? Points[nom.MarketPointID.Value].Name! : "Not Found";

                    if (Zones.TryGetValue(Meters[nom.SupplyMeterID.GetValueOrDefault()].ZoneID, out GasControlZone? zone))
                        pipeline = Pipes[zone.PipelineID].Name ?? "";

                    Deal? marketDeal = (from d in db.Deals.Include(x => x.PointSourceDeliveries) where d.Id == nom.MarketDealID select d).FirstOrDefault();

                    if (marketDeal is null)
                    {
                        ticketNum = "Deal Not Found";
                        ErrorMessage.Append($"Pipeline: {pipeline}, Nom Date: {nomDate}, Receipt Point: {receiptPoint}, Delivery Point: {deliveryPoint}, Ticket Number: {ticketNum}");
                        ErrorMessage.Append(Environment.NewLine);
                    }
                    else
                    {
                        ticketNum = marketDeal.TicketNum ?? "";
                        ErrorMessage.Append($"Pipeline: {pipeline}, Nom Date: {nomDate}, Receipt Point: {receiptPoint}, Delivery Point: {deliveryPoint}, Ticket Number: {ticketNum}");
                        ErrorMessage.Append(Environment.NewLine);
                    }
                }

                throw new Exception(ErrorMessage.ToString());
            }
        }

        public async Task<List<GasTrackExportItem>> CreateExportList(DateOnly StartDate, DateOnly EndDate)
        {
            rateCalculator = await RateCalculator.GetInstanceAsync(Enums.Product.NaturalGas);

            var db = Main.CreateContext();
            var hubCodes = (
                from mp in DataHelper.RecentMeterProductsQueryable(db)
                where mp.ProductId == (int)Enums.Product.NaturalGas && mp.HubCode != null
                select new { mp.MeterId, mp.HubCode }
            ).ToDictionary(x => x.MeterId, x => x.HubCode);

            await GetData(StartDate, EndDate);
            CheckForInvalidItems();
            var Noms = MarketSupplies;
            var ptrsList = new List<PtrAndFuelStorageItem>();
            var NormalSupplyNoms = from n in Noms
                                   where n.SupplyDealID is not null
                                   select n;
            Dictionary<SupplyDealMarketTransferPair, int> CommonSupplyCounts = (from n in NormalSupplyNoms
                                                                                where n.MarketTransferID is not null
                                                                                group n by new { n.SupplyDealID, n.MarketTransferID, n.NomDate } into Group
                                                                                select new
                                                                                {
                                                                                    Group.Key.SupplyDealID,
                                                                                    Group.Key.MarketTransferID,
                                                                                    Group.Key.NomDate,
                                                                                    SupplyCount = Group.Count()
                                                                                }).ToDictionary(n => new SupplyDealMarketTransferPair(n.SupplyDealID.GetValueOrDefault(), n.MarketTransferID.GetValueOrDefault(), n.NomDate), n => n.SupplyCount, new SupplyDealMarketTransferComparer());
            foreach (var nsn in NormalSupplyNoms)
            {
                if (nsn.MarketDealID is not null)
                {
                    var gei = new GasTrackExportItem();
                    FillGeiSupply(gei, nsn);
                    FillGeiMarket(gei, nsn);
                    gasExport.Add(gei);
                }
                // Bryce says only subtract PTR when it is not on the first piece
                // ptrsList = New List(Of PtrAndFuelStorageItem)
                // ptrsList.Add(New PtrAndFuelStorageItem(RateCalculator, LocalDeals(nsn.SupplyDealID).CounterpartyID, nsn, Nothing))
                // gei.bVOLUME = gei.bVOLUME - GetPtrToSubtract(ptrsList)
                // CreatePtrRow(nsn, False) Bryce requested that this be commented temporarily since right now they will be entering in PTR manually using a report
                else
                {
                    // transfer market deal
                    ptrsList = new List<PtrAndFuelStorageItem>();
                    GasControlMarketSupply? RealMarketDealNom = null;
                    var GasTrackTransferValues = new List<GasTrackTransferValue>();
                    int TransferNum = 0;
                    GasControlMarketSupply PreviousNom = nsn;
                    int FirstMarketTransferID = PreviousNom.MarketTransferID.GetValueOrDefault();
                    int CommonSupplyCount = CommonSupplyCounts[new SupplyDealMarketTransferPair(nsn.SupplyDealID.GetValueOrDefault(), FirstMarketTransferID, nsn.NomDate)];
                    ptrsList.Add(new PtrAndFuelStorageItem(ref rateCalculator, LocalDeals[nsn.SupplyDealID.GetValueOrDefault()].CounterpartyID.GetValueOrDefault(), PreviousNom, TransferDeals[PreviousNom.MarketTransferID.GetValueOrDefault()], nsn.SupplyMeterID.GetValueOrDefault()));
                    while (RealMarketDealNom is null)
                    {
                        TransferNum += 1;
                        GasTrackTransferValue gttv = new()
                        {
                            TransferNum = TransferNum
                        };
                        var NextNoms = from n in Noms
                                       where n.SupplyTransferID == PreviousNom.MarketTransferID && n.NomDate == PreviousNom.NomDate
                                       orderby n.SupplyTransferID, n.NomDate, n.MarketDealID, n.MarketTransferID
                                       select n;
                        if (NextNoms.Any())
                        {
                            bool IsFirstNom = true;
                            int NextNomCount = NextNoms.Count();
                            foreach (var NextNom in NextNoms) // for each since a transfer deal may go to multiple markets
                            {
                                GasControlTransferDeal NextNomSupplyTransfer = TransferDeals[NextNom.SupplyTransferID.GetValueOrDefault()];
                                if (IsFirstNom)
                                {
                                    gttv.TicketNum = NextNomSupplyTransfer.TicketNum;
                                    if (NextNomSupplyTransfer.IsMeter1Supply)
                                    {
                                        if (hubCodes.ContainsKey(NextNomSupplyTransfer.Meter2ID))
                                            gttv.OutPoint = hubCodes[NextNomSupplyTransfer.Meter2ID];
                                        else
                                            gttv.OutPoint = string.Empty;

                                        if (hubCodes.ContainsKey(NextNomSupplyTransfer.Meter1ID))
                                            gttv.InPoint = hubCodes[NextNomSupplyTransfer.Meter1ID];
                                        else
                                            gttv.InPoint = string.Empty;

                                        gttv.InPipe = GetPipeName(Zones[Meters[NextNomSupplyTransfer.Meter1ID].ZoneID].PipelineID);
                                    }
                                    else
                                    {
                                        if (hubCodes.ContainsKey(NextNomSupplyTransfer.Meter1ID))
                                            gttv.OutPoint = hubCodes[NextNomSupplyTransfer.Meter1ID];
                                        else
                                            gttv.OutPoint = string.Empty;

                                        if (hubCodes.ContainsKey(NextNomSupplyTransfer.Meter2ID))
                                            gttv.InPoint = hubCodes[NextNomSupplyTransfer.Meter2ID];
                                        else
                                            gttv.InPoint = string.Empty;

                                        gttv.InPipe = GetPipeName(Zones[Meters[NextNomSupplyTransfer.Meter2ID].ZoneID].PipelineID);
                                    }

                                    gttv.InContract = GetGasTrackContractID(NextNom.PipeContractID);
                                    gttv.Ownership = GetOwnership(NextNom.PipeContractID);
                                    GasTrackTransferValues.Add(gttv);
                                    if (!ptrsList.Any(x => x.PtrAmtPlusPtrFuelAmt.HasValue))
                                    {
                                        if (Convert.ToBoolean(NextNom.MarketTransferID.HasValue))
                                            ptrsList.Add(new PtrAndFuelStorageItem(ref rateCalculator, LocalDeals[nsn.SupplyDealID.GetValueOrDefault()].CounterpartyID.GetValueOrDefault(), NextNom, TransferDeals[NextNom.MarketTransferID.GetValueOrDefault()], nsn.SupplyMeterID.GetValueOrDefault()));
                                        else
                                            ptrsList.Add(new PtrAndFuelStorageItem(ref rateCalculator, LocalDeals[nsn.SupplyDealID.GetValueOrDefault()].CounterpartyID.GetValueOrDefault(), NextNom, null, nsn.SupplyMeterID.GetValueOrDefault()));
                                    }
                                }

                                IsFirstNom = false;

                                // reset transfer rate values since they may be different for each market deal that a single supply goes to when it goes to multiple markets via a transfer
                                if (gttv.InContract is not null)
                                {
                                    var FromMeterID = GetReceiptMeterID(NextNom);
                                    var ToMeterID = GetDeliveryMeterID(NextNom);
                                    gttv.FuelRate = rateCalculator.GetFuelPercent(NextNom.PipeContractID, NextNom.NomDate, NextNom.SupplyMeterID.GetValueOrDefault(), GetDeliveryMeterID(NextNom));
                                    bool NonJurisdictional = Pipes[Zones[Meters[FromMeterID].ZoneID].PipelineID].NonJurisdictional;
                                    bool IsAgency = Entities[LocalDeals[nsn.SupplyDealID.GetValueOrDefault()].CounterpartyID.GetValueOrDefault()].IsAgency;
                                    gttv.TransferRate = rateCalculator.GetTransRate(NextNom.PipeContractID, NextNom.NomDate, FromMeterID, ToMeterID, NonJurisdictional, IsAgency);
                                }

                                if (NextNomCount > 1 && NextNom.MarketDealID is not null) // multiple real markets
                                {
                                    RealMarketDealNom = NextNom;
                                    var gei = new GasTrackExportItem();
                                    FillGeiSupply(gei, nsn);
                                    FillGeiTransfers(gei, GasTrackTransferValues);
                                    ResetGeiSupplyVolForTransfers(gei, GasTrackTransferValues, NextNom.NominatedVolume.GetValueOrDefault());
                                    FillGeiMarket(gei, RealMarketDealNom);
                                    gasExport.Add(gei);
                                }
                                else if (NextNomCount == 1 && NextNom.MarketDealID is not null) // single real market
                                {
                                    if (CommonSupplyCount == 1) // single real supply
                                    {
                                        RealMarketDealNom = NextNom;
                                        var gei = new GasTrackExportItem();
                                        FillGeiSupply(gei, nsn);
                                        FillGeiTransfers(gei, GasTrackTransferValues);
                                        FillGeiMarket(gei, RealMarketDealNom);
                                        gei.bVOLUME = (int.Parse(gei.bVOLUME ?? "0") - GetNonFirstLegPtrToSubtract(ptrsList)).ToString();
                                        gasExport.Add(gei);
                                    }
                                    else // multiple real supplies
                                    {
                                        RealMarketDealNom = NextNom;
                                        var gei = new GasTrackExportItem();
                                        FillGeiSupply(gei, nsn);
                                        FillGeiTransfers(gei, GasTrackTransferValues);
                                        FillGeiMarket(gei, RealMarketDealNom);
                                        // Dim FirstLegPTR As Integer = GetFirstLegPtrToSubtract(ptrsList)
                                        // resets market vol by starting with the first nom in the path, subtracting fuel on every pipe, and then subtracting NonFirstLegPtr
                                        // we don't subtract the first leg PTR because the first nom's volume is actually the delivered volume on the first piece and has already had PTR subtraced from the deal volume
                                        ResetGeiMarketVolForTransfers(gei, GasTrackTransferValues, nsn.NominatedVolume.GetValueOrDefault());
                                        gei.sVOLUME = (int.Parse(gei.sVOLUME ?? "0") - GetNonFirstLegPtrToSubtract(ptrsList)).ToString();
                                        gei.bVOLUME = (int.Parse(gei.bVOLUME ?? "0") - GetNonFirstLegPtrToSubtract(ptrsList)).ToString();
                                        gasExport.Add(gei);
                                    }
                                }

                                // CreatePtrRow(nsn, True, NextNom) Bryce requested that this be commented temporarily since right now they will be entering in PTR manually using a report
                                PreviousNom = NextNom;
                            }
                        }
                        else
                        {
                            break;
                        } // this chain does not end with a real market deal so we skip this item
                    }
                }
            } // NormalSupplyNoms loop

            CreateFinancialDealRows(StartDate, EndDate);
            gasExport = (from ge in gasExport
                         orderby ge.bSTART_DATE, ge.bBOT_DEAL, ge.sSELL_DEAL
                         select ge).ToList();

            // on 5-24-2012 Bryce and Lanny decided not to group these
            // GasExport = (
            // From gel In gth.GroupExportList(GasExport)
            // Order By gel.bSTART_DATE, gel.bBOT_DEAL, gel.sSELL_DEAL
            // ).ToList

            return gasExport;
        }

        private static int GetNonFirstLegPtrToSubtract(IEnumerable<PtrAndFuelStorageItem> ptrsList)
        {
            // for each item in ptrsList, either PtrAmtPlusPtrFuelAmt has a value OR FuelPercent has a value, not both
            // ptrsList is filled with all the fuel amounts up until we find the first leg with a PTR

            // greater than 1 since Bryce says only subtract PTR when it is not on the first piece
            if (ptrsList.Any(x => x.PtrAmtPlusPtrFuelAmt.HasValue) && ptrsList.Count() > 1)
            {
                int? _ptrAndPtrFuelAmt = ptrsList.Last().PtrAmtPlusPtrFuelAmt;
                for (int i = ptrsList.Count() - 1; i >= 0; i -= 1)
                {
                    if (!ptrsList.ElementAt(i).PtrAmtPlusPtrFuelAmt.HasValue)
                    {
                        var fuelAmt = Math.Round(_ptrAndPtrFuelAmt.GetValueOrDefault() / (1 - ptrsList.ElementAt(i).FuelPercent.GetValueOrDefault()) - _ptrAndPtrFuelAmt.GetValueOrDefault(), 0);
                        _ptrAndPtrFuelAmt += Convert.ToInt32(fuelAmt);
                    }
                }

                return _ptrAndPtrFuelAmt.GetValueOrDefault();
            }
            else
                return 0;
        }

        private void CreateFinancialDealRows(DateOnly StartDate, DateOnly EndDate)
        {
            // Get financial only deals that have a physical link.
            // We will take buy deals as the financial deals, but the concept in this logic seems to be useless.
            var FinancialBuyDeals = (from d in LocalDeals.Values
                                     where d.PhysicalLinkID is not null && d.BuyButton == 1
                                     select new { FinancialID = d.DealID, d.PhysicalLinkID }).ToList();
            foreach (var fd in FinancialBuyDeals)
            {
                if (LocalDeals.ContainsKey(fd.FinancialID) && LocalDeals.ContainsKey(fd.PhysicalLinkID.GetValueOrDefault()))
                {
                    GasControlDeal FinancialDeal = LocalDeals[fd.FinancialID];
                    GasControlDeal PhysicalLinkDeal = LocalDeals[fd.PhysicalLinkID.GetValueOrDefault()];
                    var LoopDate = StartDate;
                    while (LoopDate <= EndDate)
                    {
                        GasTrackExportItem? gei = null;
                        if (FinancialDeal.StartDate <= LoopDate && LoopDate <= FinancialDeal.EndDate)
                        {
                            ValuationResult FinancialDealVal = Vals[new Pairs.IntegerDatePair(FinancialDeal.DealID, LoopDate)];
                            if (FinancialDealVal.FinancialPositionAmount != 0)
                            {
                                if (FinancialDeal.BuyButton == 1)
                                {
                                    gei ??= new GasTrackExportItem();
                                    FillGeiSupplyLink(gei, FinancialDeal, LoopDate, FinancialDealVal);
                                }
                                else if (FinancialDeal.BuyButton == -1)
                                {
                                    gei ??= new GasTrackExportItem();
                                    FillGeiMarketLink(gei, FinancialDeal, LoopDate, FinancialDealVal);
                                }
                            }
                        }

                        if (PhysicalLinkDeal.StartDate <= LoopDate && LoopDate <= PhysicalLinkDeal.EndDate)
                        {
                            ValuationResult PhysicalLinkDealVal = Vals[new Pairs.IntegerDatePair(PhysicalLinkDeal.DealID, LoopDate)];
                            if (PhysicalLinkDealVal.FinancialPositionAmount != 0)
                            {
                                if (PhysicalLinkDeal.BuyButton == 1)
                                {
                                    gei ??= new GasTrackExportItem();
                                    FillGeiSupplyLink(gei, PhysicalLinkDeal, LoopDate, PhysicalLinkDealVal);
                                }
                                else if (PhysicalLinkDeal.BuyButton == -1)
                                {
                                    gei ??= new GasTrackExportItem();
                                    FillGeiMarketLink(gei, PhysicalLinkDeal, LoopDate, PhysicalLinkDealVal);
                                }
                            }
                        }

                        if (gei is not null)
                            gasExport.Add(gei);
                        LoopDate = LoopDate.AddDays(1);
                    }
                }
            }
        }

        private void FillGeiSupplyLink(GasTrackExportItem gei, GasControlDeal SupplyDeal, DateOnly Day, ValuationResult SupplyDealVal)
        {
            var rsdv = SupplyDealVal;
            var rsd = SupplyDeal;
            var PipeContractID = GetAtmPipeContractID(rsd.PipelineID.GetValueOrDefault());
            gei.bBOT_DEAL = rsd.TicketNum;
            gei.bVENDOR = Entities[rsd.CounterpartyID.GetValueOrDefault()].GasTrackVNum;
            gei.bTYPE = GetGasTrackType(rsd.DealPurposeID, rsd.PhysicalDealTypeID);
            gei.bPIPELINE = GetPipeName(rsd.PipelineID.GetValueOrDefault());
            gei.bPOINT = ""; // Per Josh, this function is for financial deals only
            gei.bCONTRACT = GetGasTrackContractID(PipeContractID);
            gei.bTRADER = rsd.TraderInitials;
            gei.bTRADE_DATE = rsd.TradingDate?.ToString(GasTrackDateFormat);
            gei.bSTART_DATE = Day.ToString(GasTrackDateFormat);
            gei.bEND_DATE = Day.ToString(GasTrackDateFormat);
            gei.bPRICE = GetContractPrice(rsdv).ToString("F5");
            gei.bPREMDISC = rsd.PremiumOrDiscount?.ToString("F5");
            gei.bPRICE_TYPE = GetPriceType(rsd.FixedPriceButton);
            gei.bINDEX = GetPriceIndexName(rsd.PriceIndexID);
            gei.bFUEL_RATE = "";
            gei.bVOLUME = Math.Abs(rsdv.FinancialVolume).ToString();
            gei.bTRANS_RATE = "";
            gei.bINV_PRICE = (Util.Nz(gei.bPRICE) + Util.Nz(gei.bPREMDISC)).ToString("F5");
            gei.bINT_ENTITY = GetCounterpartyName(rsd.InternalEntityID);
            gei.bOWNERSHIP = GetOwnership(PipeContractID);
        }

        private void FillGeiMarketLink(GasTrackExportItem gei, GasControlDeal MarketDeal, DateOnly Day, ValuationResult MarketDealVal)
        {
            var rmdv = MarketDealVal;
            var rmd = MarketDeal;
            var PipeContractID = GetAtmPipeContractID(rmd.PipelineID.GetValueOrDefault());
            gei.sSELL_DEAL = rmd.TicketNum;
            gei.sGASTRACK = gei.sSELL_DEAL;
            gei.sCUSTOMER = Entities[rmd.CounterpartyID.GetValueOrDefault()].GasTrackCNum;
            gei.sTYPE = GetGasTrackType(rmd.DealPurposeID, rmd.PhysicalDealTypeID);
            gei.sPIPELINE = GetPipeName(rmd.PipelineID.GetValueOrDefault());
            gei.sPIPELINE_ID = rmd.PipelineID.GetValueOrDefault();
            gei.sPOINT = ""; // Per Josh, this function is for financial deals only
            gei.sCONTRACT = GetGasTrackContractID(PipeContractID);
            gei.sTRADER = rmd.TraderInitials;
            gei.sTRADE_DATE = rmd.TradingDate?.ToString(GasTrackDateFormat);
            gei.sSTART_DATE = Day.ToString(GasTrackDateFormat);
            gei.sEND_DATE = Day.ToString(GasTrackDateFormat);
            gei.sVOLUME = Math.Abs(rmdv.FinancialVolume).ToString();
            gei.sPRICE = GetContractPrice(rmdv).ToString("F5");
            gei.sPREMDISC = rmd.PremiumOrDiscount?.ToString("F5");
            gei.sPRICE_TYPE = GetPriceType(rmd.FixedPriceButton);
            gei.sINDEX = GetPriceIndexName(rmd.PriceIndexID);
            gei.sINV_PRICE = (Util.Nz(gei.sPRICE) + Util.Nz(gei.sPREMDISC)).ToString("F5");
            gei.sINT_ENTITY = GetCounterpartyName(rmd.InternalEntityID);
        }

        private int? GetAtmPipeContractID(int PipeID)
        {
            int? PipeContractID = null;
            var AtmPipeContracts = from p in PipeContracts.Values
                                   where p.PipelineID == PipeID && p.ContractID != null && p.ContractID.ToLower().Contains("atm")
                                   select p.ID;
            if (AtmPipeContracts.Any())
                PipeContractID = AtmPipeContracts.First();

            return PipeContractID;
        }

        // Sub CreatePtrRow(NormalSupplyNom As GasTrackMarketSupply, IsTransfer As Boolean, Optional TransferNom As GasTrackMarketSupply = Nothing)
        // Dim db As CustomDbContext = GetNewDataContext()

        // Dim nsn = NormalSupplyNom
        // Dim tsn = TransferNom

        // Dim NormalSupplyDeal As GasTrackDeal = LocalDeals(nsn.SupplyDealID)
        // Dim TransferSupplyDeal As GasTrackTransferDeal = Nothing

        // Dim PtrDeliveryMeter As Meter = Nothing
        // Dim PtrReceiptMeter As Meter = Nothing
        // Dim PtrReceiptPoint As Point = Nothing

        // If IsTransfer Then
        // TransferSupplyDeal = TransferDeals(tsn.SupplyTransferID)

        // If tsn.PtrDeliveryMeterID IsNot Nothing Then
        // PtrDeliveryMeter = Meters(tsn.PtrDeliveryMeterID)
        // End If
        // If tsn.SupplyMeterID IsNot Nothing Then
        // PtrReceiptMeter = Meters(tsn.SupplyMeterID)
        // End If
        // If tsn.SupplyPointID IsNot Nothing Then
        // PtrReceiptPoint = Points(tsn.SupplyPointID)
        // End If
        // Else
        // If nsn.PtrDeliveryMeterID IsNot Nothing Then
        // PtrDeliveryMeter = Meters(nsn.PtrDeliveryMeterID)
        // End If
        // If nsn.SupplyMeterID IsNot Nothing Then
        // PtrReceiptMeter = Meters(nsn.SupplyMeterID)
        // End If
        // If nsn.SupplyPointID IsNot Nothing Then
        // PtrReceiptPoint = Points(nsn.SupplyPointID)
        // End If
        // End If

        // If PtrDeliveryMeter IsNot Nothing AndAlso MeterPoints.Contains(PtrDeliveryMeter.ID) Then
        // Dim PtrDeliveryPoint = Points(MeterPoints(PtrDeliveryMeter.ID).First.PointID)

        // If PtrReceiptMeter.ID <> PtrDeliveryMeter.ID Then
        // Dim PtrPercent As Double?

        // Dim GasDay = nsn.NomDate

        // PtrPercent = RateCalculator.GetPtrPercent(GasDay, PtrReceiptMeter.ID, PtrDeliveryMeter.ID, NormalSupplyDeal.CounterpartyID)

        // If PtrPercent.HasValue AndAlso PtrPercent > 0 Then
        // Dim PtrSupplyCounterparty = Entities(NormalSupplyDeal.CounterpartyID)

        // 'find PTR supply deal
        // Dim PtrSupplyDeals = From d In LocalDeals.Values
        // Where d.TransactionTypeID = 1 AndAlso
        // d.DealPurposeID = 21 AndAlso
        // d.StartDate <= GasDay AndAlso d.EndDate >= GasDay AndAlso
        // d.CounterpartyID = PtrSupplyCounterparty.ID AndAlso
        // d.BuyButton = 1 AndAlso
        // (From p In PointSourceDeliveries(d.DealID) Select p.PointID).Contains(PtrReceiptPoint.ID)

        // 'find PTR market deal
        // Dim PtrMarketDeals = From d In LocalDeals.Values
        // Where d.TransactionTypeID = 1 AndAlso
        // d.DealPurposeID = 21 AndAlso
        // d.StartDate <= GasDay AndAlso d.EndDate >= GasDay AndAlso
        // d.CounterpartyID = PtrSupplyCounterparty.ID AndAlso
        // d.BuyButton = -1 AndAlso
        // (From p In PointSourceDeliveries(d.DealID) Select p.PointID).Contains(PtrDeliveryPoint.ID)


        // If PtrSupplyDeals.Any AndAlso PtrMarketDeals.Any Then
        // Dim pei As New GasExportItem

        // Dim PtrSupplyDeal = PtrSupplyDeals.First

        // pei.bBOT_DEAL = PtrSupplyDeal.TicketNum
        // pei.bVENDOR = Entities(PtrSupplyDeal.CounterpartyID).GasTrackVNum
        // pei.bTYPE = GetGasTrackType(PtrSupplyDeal.DealPurposeID, PtrSupplyDeal.PhysicalDealTypeID)
        // pei.bPIPELINE = GetPipeName(PtrSupplyDeal.PipelineSourceDeliveryID)
        // pei.bPOINT = Points(nsn.SupplyPointID).HubCode
        // pei.bCONTRACT = GetGasTrackContractID(nsn.PtrPipeContractID)
        // pei.bTRADER = PtrSupplyDeal.TraderInitials
        // pei.bTRADE_DATE = Format(PtrSupplyDeal.TradingDate, GasTrackDateFormat)
        // pei.bSTART_DATE = Format(nsn.NomDate, GasTrackDateFormat)
        // pei.bEND_DATE = Format(nsn.NomDate, GasTrackDateFormat)
        // pei.bVOLUME = PtrSupplyDeal.Volume
        // pei.bPRICE = 0
        // pei.bPREMDISC = PtrSupplyDeal.PremiumOrDiscount
        // pei.bPRICE_TYPE = GetPriceType(PtrSupplyDeal.FixedPriceButton)
        // pei.bINDEX = GetPriceIndexName(PtrSupplyDeal.PriceIndexID)

        // Dim FromMeterID As Integer = nsn.SupplyMeterID
        // Dim ToMeterID As Integer = nsn.PtrDeliveryMeterID
        // pei.bFUEL_RATE = RateCalculator.GetFuelPercent(nsn.PtrPipeContractID, nsn.NomDate, FromMeterID, ToMeterID).ToString
        // pei.bTRANS_RATE = GetTransRate(RateCalculator, nsn.PtrPipeContractID, nsn.NomDate, FromMeterID, ToMeterID).ToString

        // pei.bINV_PRICE = 0
        // pei.bINT_ENTITY = GetCounterpartyName(PtrSupplyDeal.InternalEntityID)

        // If IsTransfer Then
        // Dim Setter As New TransferSetter(pei, 1)
        // Setter.TSF = TransferSupplyDeal.TicketNum
        // If TransferSupplyDeal.IsMeter1Supply Then
        // Setter.TSF_OUT_POINT = Points(MeterPoints(nsn.PtrDeliveryMeterID).First.PointID).HubCode
        // Setter.TSF_IN_PIPE = GetPipeName(Zones(Meters(TransferSupplyDeal.Meter1ID).ZoneID).PipelineID)
        // Setter.TSF_IN_POINT = Points(MeterPoints(TransferSupplyDeal.Meter1ID).First.PointID).HubCode
        // Else
        // Setter.TSF_OUT_POINT = Points(MeterPoints(nsn.PtrDeliveryMeterID).First.PointID).HubCode
        // Setter.TSF_IN_PIPE = GetPipeName(Zones(Meters(TransferSupplyDeal.Meter2ID).ZoneID).PipelineID)
        // Setter.TSF_IN_POINT = Points(MeterPoints(TransferSupplyDeal.Meter2ID).First.PointID).HubCode
        // End If
        // Setter.TSF_IN_CONTRACT = GetGasTrackContractID(tsn.PtrPipeContractID)
        // Setter.TSF_OWNERSHIP = GetOwnership(tsn.PipeContractID)

        // If Setter.TSF_IN_CONTRACT IsNot Nothing Then
        // FromMeterID = PtrReceiptMeter.ID
        // ToMeterID = PtrDeliveryMeter.ID
        // Setter.TSF_FUEL_RATE = RateCalculator.GetFuelPercent(tsn.PtrPipeContractID, tsn.NomDate, PtrReceiptMeter.ID, PtrDeliveryMeter.ID).ToString
        // Setter.TSF_TRANS_RATE = GetTransRate(RateCalculator, tsn.PtrPipeContractID, tsn.NomDate, FromMeterID, ToMeterID).ToString
        // End If

        // Setter.SetItemValues()
        // End If

        // Dim PtrMarketDeal = PtrMarketDeals.First

        // pei.sSELL_DEAL = PtrMarketDeal.TicketNum
        // pei.sGASTRACK = pei.sSELL_DEAL
        // pei.sCUSTOMER = Entities(PtrMarketDeal.CounterpartyID).GasTrackCNum
        // pei.sTYPE = GetGasTrackType(PtrMarketDeal.DealPurposeID, PtrMarketDeal.PhysicalDealTypeID)
        // pei.sPIPELINE = GetPipeName(PtrMarketDeal.PipelineSourceDeliveryID)
        // pei.sPOINT = Points(PointSourceDeliveries(PtrMarketDeal.DealID).First.PointID).HubCode

        // If IsTransfer Then
        // pei.sCONTRACT = GetGasTrackContractID(tsn.PtrPipeContractID)
        // Else
        // pei.sCONTRACT = GetGasTrackContractID(nsn.PtrPipeContractID)
        // End If

        // pei.sTRADER = PtrMarketDeal.TraderInitials
        // pei.sTRADE_DATE = Format(PtrMarketDeal.TradingDate, GasTrackDateFormat)
        // pei.sSTART_DATE = Format(nsn.NomDate, GasTrackDateFormat)
        // pei.sEND_DATE = Format(nsn.NomDate, GasTrackDateFormat)
        // pei.sVOLUME = PtrMarketDeal.Volume
        // pei.sPRICE = 0
        // pei.sPREMDISC = PtrMarketDeal.PremiumOrDiscount
        // pei.sPRICE_TYPE = GetPriceType(PtrMarketDeal.FixedPriceButton)
        // pei.sINDEX = GetPriceIndexName(PtrMarketDeal.PriceIndexID)
        // pei.sINV_PRICE = 0
        // pei.sINT_ENTITY = GetCounterpartyName(PtrMarketDeal.InternalEntityID)

        // GasExport.Add(pei)
        // End If
        // End If
        // End If
        // End If
        // End Sub
    }
}
