using System;
using System.Runtime.CompilerServices;
using UnityEngine;

namespace Jovian.TagSystem {
    /// <summary>
    /// Lightweight tag identity. 8 bytes, no heap allocation.
    /// Hierarchy queries are resolved via GameTagManager static lookups.
    /// </summary>
    [Serializable]
    public struct JovianTag : IEquatable<JovianTag>, IComparable<JovianTag> {
        [SerializeField] private int id;
        [SerializeField] private int parentId;

        public int Id => id;
        public int ParentId => parentId;

        public JovianTag(int id, int parentId) {
            this.id = id;
            this.parentId = parentId;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public readonly bool IsNone() => id == 0;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public readonly bool IsValid() => id > 0;

        /// <summary>
        /// Checks if this tag is a descendant of the given ancestor.
        /// Walks the parent chain via GameTagManager.
        /// </summary>
        public readonly bool IsDescendantOf(JovianTag ancestor) {
            if(id == 0 || ancestor.id == 0) return false;
            if(id == ancestor.id) return true;

            // Walk up from this tag's parent chain
            var current = this;
            while(current.parentId != 0) {
                if(current.parentId == ancestor.id) return true;
                current = JovianTagsHandler.GetTag(current.parentId);
            }
            return false;
        }

        /// <summary>
        /// Checks if this tag is an ancestor of the given descendant.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public readonly bool IsAncestorOf(JovianTag descendant) {
            return descendant.IsDescendantOf(this);
        }

        /// <summary>
        /// Checks if this tag shares the same parent as the given tag.
        /// </summary>
        public readonly bool IsSiblingTo(JovianTag sibling) {
            if(id == 0 || sibling.id == 0) return false;
            return parentId == sibling.parentId;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator ==(JovianTag x, JovianTag y) => x.id == y.id;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator !=(JovianTag x, JovianTag y) => x.id != y.id;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public readonly bool Equals(JovianTag other) => id == other.id;

        public readonly int CompareTo(JovianTag other) => id.CompareTo(other.id);

        public override readonly bool Equals(object obj) => obj is JovianTag other && id == other.id;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public override readonly int GetHashCode() => id;

        public override readonly string ToString() {
            if(JovianTagsHandler.IsInitialized) {
                return JovianTagsHandler.TagToString(this);
            }
            return id == 0 ? "None" : $"Tag({id})";
        }
    }
}
