using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Serialization;

namespace Jovian.TagSystem {
    [CreateAssetMenu(menuName = "Jovian/Tag System/JovianTagSettings")]
    public class JovianTagsSettings : ScriptableObject {
        public RegisteredTag[] gameTags = Array.Empty<RegisteredTag>();

        private static readonly System.Text.RegularExpressions.Regex ValidSegment =
            new(@"^[A-Za-z][A-Za-z0-9_]*$");

        private static readonly HashSet<string> CSharpKeywords = new(StringComparer.Ordinal) {
            "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char",
            "checked", "class", "const", "continue", "decimal", "default", "delegate", "do",
            "double", "else", "enum", "event", "explicit", "extern", "false", "finally",
            "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int",
            "interface", "internal", "is", "lock", "long", "namespace", "new", "null",
            "object", "operator", "out", "override", "params", "private", "protected",
            "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof",
            "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true",
            "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
            "virtual", "void", "volatile", "while"
        };

        public void AddGameTag(string newTag) {
            List<string> tagHierarchy = newTag.Split(JovianTagsHandler.tagDelimiter).ToList();
            tagHierarchy.Remove("");
            newTag = string.Join(JovianTagsHandler.tagDelimiter, tagHierarchy);

            // Validate each segment
            foreach(var segment in tagHierarchy) {
                if(!ValidSegment.IsMatch(segment)) {
                    Debug.LogError($"Invalid tag segment '{segment}': must start with a letter and contain only letters, digits, or underscores.");
                    return;
                }
                if(CSharpKeywords.Contains(segment)) {
                    Debug.LogError($"Invalid tag segment '{segment}': is a C# reserved keyword.");
                    return;
                }
            }

            if(gameTags.Contains(new(newTag))) {
                Debug.LogError($"{newTag} is already added to the game");
                return;
            }

            string[] tagsSplit = newTag.Split(JovianTagsHandler.tagDelimiter);
            string tagToAdd = "";
            for(int i = 0, n = tagsSplit.Length; i < n; i++) {
                tagToAdd += tagsSplit[i];
                if(gameTags.Any((a) => a.tag == tagToAdd)) {
                    tagToAdd += ".";
                    continue;
                }

                var previous = gameTags;
                gameTags = new RegisteredTag[gameTags.Length + 1];
                previous.CopyTo(gameTags, 0);
                gameTags[^1] = new RegisteredTag(tagToAdd);
                tagToAdd += ".";
            }
#if UNITY_EDITOR
            UnityEditor.EditorUtility.SetDirty(this);
#endif
        }

        private static bool IsValidSegment(string segment) {
            return !string.IsNullOrWhiteSpace(segment)
                && ValidSegment.IsMatch(segment)
                && !CSharpKeywords.Contains(segment);
        }

        private static bool IsValidTag(string tag) {
            if(string.IsNullOrWhiteSpace(tag)) return false;
            return tag.Split(JovianTagsHandler.tagDelimiter).All(IsValidSegment);
        }

        public void RemoveTag(string gameTagToRemove) {
            var tagSearch = gameTags.ToList();
            tagSearch.RemoveAll((a) => a.tag.StartsWith(gameTagToRemove));
            gameTags = tagSearch.ToArray();
#if UNITY_EDITOR
            UnityEditor.EditorUtility.SetDirty(this);
#endif
        }
    }
}
