using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;

namespace CoolGameX.Setup
{
    /// <summary>
    /// Writes <c>CoolGameX.Ids</c>: constants for every achievement and DLC slug, so
    /// <c>CoolGameXManager.Unlock(Ids.Achievements.SpeedRunner)</c> is checked by the
    /// compiler instead of failing silently on a typo.
    /// </summary>
    internal static class IdGenerator
    {
        internal const string DefaultPath = "Assets/CoolGameX/Generated/CoolGameXIds.cs";

        internal struct Item
        {
            public string slug;
            public string name;
        }

        internal static string Generate(string gameSlug, IList<Item> achievements, IList<Item> dlc)
        {
            var sb = new StringBuilder();
            sb.AppendLine("// <auto-generated>");
            sb.AppendLine($"// Generated by Cool GameX Setup for \"{gameSlug}\" on {DateTime.Now:yyyy-MM-dd HH:mm}.");
            sb.AppendLine("// Re-generate from Tools > Cool GameX > Setup after adding achievements or DLC.");
            sb.AppendLine("// </auto-generated>");
            sb.AppendLine("namespace CoolGameX");
            sb.AppendLine("{");
            sb.AppendLine($"    /// <summary>Type-safe IDs for {Xml(gameSlug)}.</summary>");
            sb.AppendLine("    public static partial class Ids");
            sb.AppendLine("    {");
            sb.AppendLine($"        public const string Game = \"{Str(gameSlug)}\";");
            Group(sb, "Achievements", achievements);
            Group(sb, "DLC", dlc);
            sb.AppendLine("    }");
            sb.AppendLine("}");
            return sb.ToString();
        }

        internal static void Write(string code, string path = DefaultPath)
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
            File.WriteAllText(path, code, new UTF8Encoding(false));
            AssetDatabase.ImportAsset(path);
        }

        private static void Group(StringBuilder sb, string name, IList<Item> items)
        {
            sb.AppendLine();
            sb.AppendLine($"        public static class {name}");
            sb.AppendLine("        {");
            var used = new HashSet<string> { name, "Game" };
            foreach (var it in items)
            {
                if (string.IsNullOrEmpty(it.slug)) continue;
                string id = Ident(it.slug);
                string unique = id;
                for (int n = 2; !used.Add(unique); n++) unique = id + n;
                if (!string.IsNullOrEmpty(it.name)) sb.AppendLine($"            /// <summary>{Xml(it.name)}</summary>");
                sb.AppendLine($"            public const string {unique} = \"{Str(it.slug)}\";");
            }
            sb.AppendLine("        }");
        }

        /// <summary>"speed_runner" → "SpeedRunner", "1st-place" → "_1stPlace".</summary>
        internal static string Ident(string slug)
        {
            var sb = new StringBuilder();
            bool upper = true;
            foreach (char c in slug)
            {
                if (char.IsLetterOrDigit(c))
                {
                    sb.Append(upper ? char.ToUpperInvariant(c) : c);
                    upper = false;
                }
                else upper = true;
            }
            if (sb.Length == 0) return "Item";
            if (char.IsDigit(sb[0])) sb.Insert(0, '_');
            return sb.ToString();
        }

        private static string Str(string s) => (s ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"");
        private static string Xml(string s) => (s ?? "").Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("\n", " ");
    }
}
