using System;
using System.Collections.Generic;
using System.Linq;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudDb.Model;

namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors;

public class DetectorDrilling : DetectorAbstract
{
    private const double dispersionOfNormalizedRotorSpeedThreshold = 0.2d;
    public const string ExtraDataKeyHasOscillation = "hasOscillation";
    public const string ExtraDataKeyDispersionOfNormalizedRotorSpeed = "dispersionOfNormalizedRotorSpeed";
    public const string ExtraDataKeyAvgRotorSpeed = "avgRotorSpeed";

    protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperationDto? previousOperation)
   {
      var point0 = telemetry[position];
      var delta = point0.WellDepth - point0.BitDepth;
      if (delta > 0.03d)
         return false;

      if (point0.Pressure < 18)
         return false;

      return true;
   }

   protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperationDto? previousOperation)
   {
      var point0 = telemetry[position];
      var delta = point0.WellDepth - point0.BitDepth;
      
      if (delta > 0.03d)
         return IdReasonOfEnd_DeltaDepthIsHi;

      if (point0.Pressure < 18)
         return IdReasonOfEnd_PressureIsLo;

      return IdReasonOfEnd_NotDetected;
   }
   
   protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
      => CalcRop(telemetry, begin, end);

   protected override bool IsValidOperationDetectorResult(OperationDetectorResult operationDetectorResult) =>
        base.IsValidOperationDetectorResult(operationDetectorResult) 
        && (operationDetectorResult.Operation.DepthEnd - operationDetectorResult.Operation.DepthStart) > 0.01;

    protected override (int Begin, int End) RefineEdges(DetectableTelemetry[] telemetry, int begin, int end)
    {
        var i = end;        
      for (; i > begin + 1; i--)
            if (telemetry[i].WellDepth - telemetry[i - 1].WellDepth > 0.001d)
            break;

        return (begin, i);
    }

    protected override (int IdCategory, IDictionary<string, object> ExtraData) GetSpecificInformation(DetectableTelemetry[] telemetry, int begin, int end)
    {
        var (avgRotorSpeed, dispersionOfNormalizedRotorSpeed) = CalcCriteries(telemetry, begin, end);
        var idCategory = GetIdOperation(avgRotorSpeed, dispersionOfNormalizedRotorSpeed);
      var extraData = new Dictionary<string, object>
        {
            [ExtraDataKeyAvgRotorSpeed] = avgRotorSpeed,
            [ExtraDataKeyDispersionOfNormalizedRotorSpeed] = dispersionOfNormalizedRotorSpeed,
            [ExtraDataKeyHasOscillation] = avgRotorSpeed > 1 && dispersionOfNormalizedRotorSpeed > dispersionOfNormalizedRotorSpeedThreshold
        };
      return (idCategory, extraData);
    }

   private static (double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed) CalcCriteries(DetectableTelemetry[] telemetry, int begin, int end)
    {
      var telemetryRange = telemetry[begin..end];
      var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed);
        var dispersion = telemetryRange.Average(t => Math.Pow(t.RotorSpeed / avgRotorSpeed - 1, 2));
        return (avgRotorSpeed, dispersion);
    }

    private static int GetIdOperation(double avgRotorSpeed, double dispersionOfNormalizedRotorSpeed)
   {
        const int idSlideWithOscillation = WellOperationCategory.IdSlide;

        if (avgRotorSpeed < 5)
            return WellOperationCategory.IdSlide;

      if(dispersionOfNormalizedRotorSpeed < dispersionOfNormalizedRotorSpeedThreshold)
         return WellOperationCategory.IdRotor;

      return idSlideWithOscillation;
   }

}