using Speckle.Converters.Common;
using Speckle.Converters.Common.Objects;
using Speckle.Objects;
using Speckle.Sdk.Models;

namespace Speckle.Converters.Rhino.ToSpeckle.Raw;

public class ArcCurveToSpeckleConverter : ITypedConverter<RG.ArcCurve, ICurve>, ITypedConverter<RG.ArcCurve, Base>
{
  private readonly ITypedConverter<RG.Circle, SOG.Circle> _circleConverter;
  private readonly ITypedConverter<RG.Arc, SOG.Arc> _arcConverter;
  private readonly ITypedConverter<RG.Interval, SOP.Interval> _intervalConverter;
  private readonly IConverterSettingsStore<RhinoConversionSettings> _settingsStore;

  public ArcCurveToSpeckleConverter(
    ITypedConverter<RG.Circle, SOG.Circle> circleConverter,
    ITypedConverter<RG.Arc, SOG.Arc> arcConverter,
    ITypedConverter<RG.Interval, SOP.Interval> intervalConverter,
    IConverterSettingsStore<RhinoConversionSettings> settingsStore
  )
  {
    _circleConverter = circleConverter;
    _arcConverter = arcConverter;
    _intervalConverter = intervalConverter;
    _settingsStore = settingsStore;
  }

  /// <summary>
  /// Converts an RG.ArcCurve to an ICurve.
  /// </summary>
  /// <param name="target">The RG.ArcCurve to convert.</param>
  /// <returns>The converted ICurve.</returns>
  /// <remarks>
  /// ⚠️ If the provided ArcCurve is a complete circle, a Speckle Circle will be returned.
  /// Otherwise, the output will be a Speckle Arc. <br/>
  /// ✅ This method preserves the domain of the original ArcCurve.<br/>
  /// </remarks>
  public ICurve Convert(RG.ArcCurve target)
  {
    var tolerance = _settingsStore.Current.Document.ModelAbsoluteTolerance;

    if (target.IsCompleteCircle)
    {
      target.TryGetCircle(out var getObj, tolerance);
      var cir = _circleConverter.Convert(getObj);
      cir.domain = _intervalConverter.Convert(target.Domain);
      return cir;
    }

    var arc = _arcConverter.Convert(target.Arc);
    arc.domain = _intervalConverter.Convert(target.Domain);
    return arc;
  }

  // POC: CNX-9275 Need to implement this because ICurve and Base are not related, this one is needed at the top-level, the other is for better typed experience.
  //      This also causes us to have to force cast ICurve to Base as a result, which is expected to always succeed but not nice.
  /// <inheritdoc cref="Convert"/>
  /// <returns> The converted ICurve with a cast to <see cref="Base"/> object</returns>
  Base ITypedConverter<RG.ArcCurve, Base>.Convert(RG.ArcCurve target) => (Base)Convert(target);
}
