using netDxf;
using netDxf.Entities;
using netDxf.Header;
using netDxf.Units;
using TraceCad.Core.Model;
using TraceCad.Core.Validation;
using DxfArc = netDxf.Entities.Arc;
using DxfCircle = netDxf.Entities.Circle;
using DxfLine = netDxf.Entities.Line;
using DxfLayer = netDxf.Tables.Layer;
using DxfVector3 = netDxf.Vector3;

namespace TraceCad.Dxf;

public sealed class NetDxfExporter : IDxfExporter
{
    public void Export(SketchDocument document, string filePath)
    {
        ArgumentNullException.ThrowIfNull(document);
        ArgumentException.ThrowIfNullOrWhiteSpace(filePath);

        var errors = DrawingValidator.Validate(document)
            .Where(issue => issue.Severity == DrawingIssueSeverity.Error)
            .ToList();
        if (errors.Count > 0)
        {
            throw new InvalidOperationException($"Cannot export invalid drawing: {errors[0].Message}");
        }

        var dxf = new DxfDocument();
        dxf.DrawingVariables.InsUnits = DrawingUnits.Millimeters;

        foreach (var layer in document.Layers.Where(layer => layer.Exportable))
        {
            if (!dxf.Layers.Contains(layer.Name))
            {
                dxf.Layers.Add(new DxfLayer(layer.Name));
            }
        }

        foreach (var entity in document.Entities)
        {
            var layer = document.FindLayer(entity.Layer);
            if (layer is { Exportable: false })
            {
                continue;
            }

            dxf.Entities.Add(ToDxfEntity(entity));
        }

        dxf.Save(filePath);
    }

    private static EntityObject ToDxfEntity(Entity entity)
    {
        return entity switch
        {
            LineEntity line => ApplyLayer(
                new DxfLine(ToVector(line.Start.X, line.Start.Y), ToVector(line.End.X, line.End.Y)),
                line.Layer),

            CircleEntity circle => ApplyLayer(
                new DxfCircle(ToVector(circle.Center.X, circle.Center.Y), circle.Radius),
                circle.Layer),

            ArcEntity arc => ApplyLayer(
                new DxfArc(
                    ToVector(arc.Center.X, arc.Center.Y),
                    arc.Radius,
                    ExportStartAngle(arc),
                    ExportEndAngle(arc)),
                arc.Layer),

            _ => throw new NotSupportedException($"Unsupported entity type '{entity.GetType().Name}'.")
        };
    }

    private static T ApplyLayer<T>(T entity, string layerName)
        where T : EntityObject
    {
        entity.Layer = new DxfLayer(layerName);
        return entity;
    }

    private static double ExportStartAngle(ArcEntity arc) =>
        arc.IsClockwise ? arc.EndAngleDeg : arc.StartAngleDeg;

    private static double ExportEndAngle(ArcEntity arc) =>
        arc.IsClockwise ? arc.StartAngleDeg : arc.EndAngleDeg;

    private static DxfVector3 ToVector(double x, double y) => new(x, y, 0.0);
}
