using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[SelectionBase]
public class SnapPoint : MonoBehaviour
{
    public SnapPoint SnapTarget;
    public SnappableRoom _snappableRoom => GetComponentInParent<SnappableRoom>();

    [ContextMenu("SnapToMyTarget")]
    public void SnapToMySnapTarget() => SnapTo(SnapTarget);
    
    public void SnapTo(SnapPoint other)
    {
        Undo.RecordObject(_snappableRoom.transform, "SnapTo");
        var offset = _snappableRoom.transform.position - transform.position;
        var newPosition = other.transform.position + offset;
        _snappableRoom.transform.position = newPosition;

        OpenChildHallParts(this);
        OpenChildHallParts(other);
    }

    void OpenChildHallParts(SnapPoint other)
    {
        var hallPartToOpen = other.GetComponentInChildren<HallPart>();
        if (hallPartToOpen != null)
            hallPartToOpen.SetMode(OpeningMode.Open);
    }

    public void AlignTo(SnapPoint other)
    {
        var rotationOffset = transform.rotation.eulerAngles.y - _snappableRoom.transform.rotation.eulerAngles.y;
        Undo.RecordObject(_snappableRoom.transform, "AlignTo");
        _snappableRoom.transform.rotation = other.transform.rotation;
        _snappableRoom.transform.Rotate(0, 180, 0);
        _snappableRoom.transform.Rotate(0, -rotationOffset, 0);
    }

    [ContextMenu("Align and Snap To Me &m")]
    public void SnapAndAlignOtherToMe()
    {
        if (SnapTarget != null)
        {
            SnapTarget.AlignTo(this);
            SnapTarget.SnapTo(this);
        }
    }

    [ContextMenu("Connect 2 SnapPoints")]
    public void Connect2()
    {
        if (Selection.objects.Length != 2)
        {
            Debug.LogError("Connect 2 requires exactly 2 snap points be selected");
            return;
        }

        var snapO1 = Selection.objects[0];
        var snap1 = (snapO1 as GameObject)?.GetComponent<SnapPoint>();
        var snapO2 = Selection.objects[1];
        var snap2 = (snapO2 as GameObject)?.GetComponent<SnapPoint>();
        if (snap1 && snap2)
        {
            snap2.SnapTarget = snap1;
            snap1.SnapTarget = snap2;
        }
    }

    [ContextMenu("Snap And Align to Other")]
    public void SnapAndAlign()
    {
        if (SnapTarget != null)
        {
            AlignTo(SnapTarget);
            SnapTo(SnapTarget);
        }
    }

    void OnDrawGizmos()
    {
        Gizmos.color = UnityEngine.Color.green;
        if (SnapTarget != null)
            Gizmos.DrawLine(transform.position, SnapTarget.transform.position);
    }

    void OnValidate()
    {
        if (SnapTarget == null)
            SnapTarget = null;

        bool showVisuals = true;
        foreach (var child in transform.GetComponentsInChildren<Transform>(true))
            if (child.name == "Snap Point Visualizer")
                child.gameObject.SetActive(showVisuals);
    }

    public SnapPoint FindNearestOther(IEnumerable<SnapPoint> others, float maxDistance = 10f)
    {
        SnapPoint closest = null;
        var closestDistance = float.MaxValue;
        foreach (var other in others)
        {
            if (other == this || other._snappableRoom == _snappableRoom)
                continue;

            if (other.SnapTarget != null)
                continue;

            var dist = Vector3.Distance(transform.position, other.transform.position);
            if (dist > maxDistance)
                continue;

            if (dist < closestDistance)
            {
                closestDistance = dist;
                closest = other;
            }
        }

        return closest;
    }
}