using System;
using AsbCloudApp.Data.ProcessMaps.Report;
using AsbCloudApp.Data.SAUB;

namespace AsbCloudInfrastructure.Services.ProcessMaps.Report.Data;

internal class ParamStat
{
    private double spWSum;
    private double pvWSum;
    private double limitMaxWSum;

    private double deltaDepthSum;

    private readonly Func<TelemetryDataSaubStatDto, double> getterSp;
    private readonly Func<TelemetryDataSaubStatDto, double> getterPv;
    private readonly Func<TelemetryDataSaubStatDto, double>? getterLimitMax;

    private readonly int idFeedRegulator;
    private readonly int idMode;
    private TelemetryDataSaubStatDto? previous;

    public double SpUsageDepth { get; private set; }
    private static double spUsageTotal;

    public ParamStat(Func<TelemetryDataSaubStatDto, double> getterSp,
       Func<TelemetryDataSaubStatDto, double> getterPv,
       Func<TelemetryDataSaubStatDto, double>? getterLimitMax,
       int idFeedRegulator,
       int idMode)
    {
        this.getterSp = getterSp;
        this.getterPv = getterPv;
        this.getterLimitMax = getterLimitMax;
        this.idFeedRegulator = idFeedRegulator;
        this.idMode = idMode;
        spUsageTotal = 0d;
    }

    public void UpdateStat(TelemetryDataSaubStatDto current)
    {
        if (previous is not null)
        {
            var deltaDepth = current.WellDepthMin - previous.WellDepthMin;
            if (deltaDepth > 0)
            {
                var deltaDepthHalf = deltaDepth / 2;

                double CalculateWeight(Func<TelemetryDataSaubStatDto, double> getter) =>
                   (getter(previous!) + getter(current)) * deltaDepthHalf;

                spWSum += CalculateWeight(getterSp);
                pvWSum += CalculateWeight(getterPv);
                if (getterLimitMax is not null)
                    limitMaxWSum += CalculateWeight(getterLimitMax!);

                if (current.IdFeedRegulator is not null)
                    if (current.IdFeedRegulator == idFeedRegulator)
                    {
                        SpUsageDepth += deltaDepth;
                        spUsageTotal += deltaDepth;
                    }
                    else
                    {
                        var pvErr = (getterSp(current) - getterPv(current)) / getterSp(current);
                        if (pvErr < 0.03d) //3%
                        {
                            SpUsageDepth += deltaDepth;
                            spUsageTotal += deltaDepth;
                        }
                    }

                deltaDepthSum += deltaDepth;
            }
        }

        previous = current;
    }

    public ProcessMapReportWellDrillingParamsDto MakeParams(double? spPlan)
    {
        var result = new ProcessMapReportWellDrillingParamsDto
        {
            SetpointPlan = spPlan,
            Fact = DivideValByDepth(pvWSum),
        };

        if (idMode == 0)
        {
            result.SetpointFact = null;
            result.Limit = null;
            result.SetpointUsage = null;
        }
        else
        {
            result.SetpointFact = DivideValByDepth(spWSum);
            result.Limit = getterLimitMax is not null ? DivideValByDepth(limitMaxWSum) : null;
            result.SetpointUsage = deltaDepthSum > 0d ? 100d * SpUsageDepth / spUsageTotal : null;
        }

        return result;
    }

    private double? DivideValByDepth(double? val)
    {
        if (val is null || val == 0d || deltaDepthSum == 0d)
            return null;
        return val / deltaDepthSum;
    }
}