using Fast.Logic.RateControl;
using SosItem = Fast.Models.Crude.SosItem;

namespace Fast.Web.Logic;

public class SosCrudeHelper
{
    public SosCrudeHelper()
    {
    }

    internal static async Task<List<SosPipeContractInfo>> GetPipeContracts(List<int> pipeIds, DateOnly nomDate)
    {
        List<SosPipeContractInfo> result = new();
        using var db = Main.CreateContext();

        result = await (
          from pc in db.PipelineContracts
          where pipeIds.Contains(pc.PipelineId) && pc.StartDate <= nomDate && pc.EndDate >= nomDate
            && pc.Product.CategoryId == (int)Enums.ProductCategory.CrudeOil
          orderby pc.ContractId
          select new SosPipeContractInfo
          {
              ContractId = pc.Id,
              ContractName = pc.ContractId,
              IsPtrContract = pc.IsPtrContract,
              PipeId = pc.PipelineId,
              ProductId = pc.ProductId
          }).AsNoTracking().ToListAsync();

        return result;
    }

    internal static double CalcPtrPercent(DateOnly nomDate, double? originalPtrPercent, int fromMeterId, int? toMeterId, int? supplyCounterpartyID, int? supplyDealId, int? supplyTransferDealId, int? pipeContractId, RateCalculator rateCalculator)
    {
        double ptrPercent;
        if (originalPtrPercent.HasValue)
            ptrPercent = originalPtrPercent.Value;
        else
            ptrPercent = rateCalculator.GetPtrPercent(nomDate, fromMeterId, toMeterId, supplyCounterpartyID, supplyDealId, supplyTransferDealId, pipeContractId).GetValueOrDefault();

        return ptrPercent;
    }

    internal static double GetAmountOfPercent(double? amountToGetPercentOf, double? percent)
    {
        double amountOfPercent = amountToGetPercentOf.GetValueOrDefault() / (1 - percent.GetValueOrDefault()) - amountToGetPercentOf.GetValueOrDefault();
        amountOfPercent = Math.Round(amountOfPercent, 0, MidpointRounding.AwayFromZero);
        return amountOfPercent;
    }

    internal static double CalcAvgSupplyNomFuelPercent(DateOnly nomDate, int receiptMeterId, int? pipeContractId, List<VolumeMeterPair> volMeterPairs, RateCalculator rateCalculator)
    {
        if (!volMeterPairs.Any())
            return 0;

        double avgFuelpercent = 0;
        double totalVolume = (double)volMeterPairs.Sum(x => x.Volume ?? 0);
        if (totalVolume > 0)
        {
            double amount = 0;
            foreach (var nom in volMeterPairs)
            {
                if (nom.DeliveryMeterId != 0)
                {
                    double currentMeterFuelPercent = rateCalculator.GetFuelPercent(pipeContractId, nomDate, receiptMeterId, nom.DeliveryMeterId).GetValueOrDefault();
                    double currentMeterVol = nom.Volume.GetValueOrDefault();
                    amount += currentMeterFuelPercent * currentMeterVol;
                }
            }
            avgFuelpercent = amount / totalVolume;
        }

        return avgFuelpercent;
    }

    internal static double CalcSingleNomFuelPercent(DateOnly nomDate, int? deliveryVol, int? pipeContractId, int receiptMeterId, int deliveryMeterId, RateCalculator rateCalculator)
    {
        if (!deliveryVol.HasValue || deliveryVol.Value <= 0)
            return 0;

        double fuelpercent = rateCalculator.GetFuelPercent(pipeContractId, nomDate, receiptMeterId, deliveryMeterId).GetValueOrDefault();
        return fuelpercent;
    }

    internal static async Task<List<IdName>> GetDeliveryMeters(int pipelineId, DateOnly nomDate)
    {
        List<int> meterIds = await GetMetersForPipeline(pipelineId);
        List<IdName> result = (await DataHelper.GetMetersByProductAsync(Enums.ProductCategory.CrudeOil, meterIds, nomDate)).Select(x => new IdName(x.MeterId, x.MeterNameAndNum)).ToList();
        return result;
    }

    internal static async Task<List<int>> GetMetersForPipeline(int pipelineId)
    {
        var productId = (int)Enums.Product.CrudeOil;
        using var db = Main.CreateContext();
        var asOfDate = DateOnly.FromDateTime(DateTime.Now.Date);

        List<int> meterIds = await (
            from m in db.Meters
            let mp = m.MeterProducts
                .Where(x => x.ProductId == productId && x.EffectiveDate <= asOfDate).OrderByDescending(x => x.EffectiveDate).FirstOrDefault()
            where mp != null
            let zone = mp.SourceZone
            where zone != null
                && zone.PipelineId == pipelineId
            select m.Id
        ).ToListAsync();

        return meterIds;
    }

    internal static List<int> GetValidReceiptMeterIds(List<SosItem> items)
    {
        return items.Select(x => x.ReceiptMeterId ?? 0).Distinct().ToList();
    }

    internal static async Task<Dictionary<int, string>> GetPointNamesAsync()
    {
        using var db = Main.CreateContext();
        var result = await (
          from x in db.Points
          orderby x.Id
          select new { x.Id, x.Name }
        ).ToDictionaryAsync(x => x.Id, x => x.Name);

        return result;
    }

    internal class VolumeMeterPair
    {
        public int? Volume { get; set; }
        public int DeliveryMeterId { get; set; }

        public VolumeMeterPair(int? volume, int deliveryMeterId)
        {
            Volume = volume;
            DeliveryMeterId = deliveryMeterId;
        }
    }
}
