using System;
using UnityEngine;

namespace Jovian.TagSystem {
    /// <summary>
    /// Serializable tag selection for use in MonoBehaviours and ScriptableObjects.
    /// Always supports multiple tags. Use the property drawer to select tags in the inspector.
    /// </summary>
    [Serializable]
    public struct JovianTagsGroup {
        [SerializeField] public string[] tags;

        public JovianTagsGroup(params string[] tags) {
            this.tags = tags ?? Array.Empty<string>();
        }

        public int Count => tags?.Length ?? 0;

        /// <summary>
        /// Returns all selected tags as resolved GameTags.
        /// Stale/unregistered tag names are silently skipped.
        /// </summary>
        public JovianTagsContainer ToContainer() {
            if(tags == null || tags.Length == 0) {
                return JovianTagsContainer.Empty;
            }
            var container = new JovianTagsContainer(tags.Length);
            foreach(var tag in tags) {
                if(JovianTagsHandler.TryGetGameTagThatIsNotNone(tag, out var resolved)) {
                    container.Add(resolved);
                }
            }
            return container;
        }

        /// <summary>
        /// Checks if any selected tag matches the given tag exactly.
        /// Stale/unregistered tag names count as no match (no error log).
        /// </summary>
        public bool Contains(JovianTag jovianTag) {
            if(tags == null) return false;
            foreach(var tag in tags) {
                if(JovianTagsHandler.TryGetGameTag(tag, out var resolved)
                    && resolved.Equals(jovianTag)) {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Checks if any selected tag is a descendant of the given ancestor.
        /// </summary>
        public bool ContainsDescendantOf(JovianTag ancestor) {
            if(tags == null) return false;
            foreach(var tag in tags) {
                if(JovianTagsHandler.TryGetGameTag(tag, out var resolved)
                    && resolved.IsDescendantOf(ancestor)) {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Checks if any selected tag is an ancestor of the given descendant.
        /// </summary>
        public bool ContainsAncestorOf(JovianTag descendant) {
            if(tags == null) return false;
            foreach(var tag in tags) {
                if(JovianTagsHandler.TryGetGameTag(tag, out var resolved)
                    && resolved.IsAncestorOf(descendant)) {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Checks if any selected tag is a sibling of the given tag.
        /// </summary>
        public bool ContainsSiblingOf(JovianTag sibling) {
            if(tags == null) return false;
            foreach(var tag in tags) {
                if(JovianTagsHandler.TryGetGameTag(tag, out var resolved)
                    && resolved.IsSiblingTo(sibling)) {
                    return true;
                }
            }
            return false;
        }

        public bool HasAny() {
            return tags != null && tags.Length > 0;
        }

        public override string ToString() {
            if(tags == null || tags.Length == 0) return "None";
            return string.Join(", ", tags);
        }
    }
}
