// Asset Naming Manager — ready-made conventions so nobody starts from a blank page.
using System.Collections.Generic;

namespace AssetNamingManager
{
    /// <summary>The starting conventions shipped with the tool.</summary>
    public enum NamingPreset
    {
        /// <summary>Plain PascalCase everywhere, no prefixes. What most Unity projects drift towards anyway.</summary>
        UnityStandard = 0,

        /// <summary>T_ / M_ / SM_ / BP_ prefixes. Familiar to anyone who has shipped in Unreal.</summary>
        UnrealStyle = 1,

        /// <summary>Tight folders and short paths, tuned for 2D mobile projects.</summary>
        Mobile2D = 2,

        /// <summary>Only the things nobody argues about: no spaces, no junk words, no duplicates.</summary>
        Minimal = 3,

        /// <summary>Everything on, everything an error. For a team that already agrees on a convention.</summary>
        StrictStudio = 4
    }

    /// <summary>Builds the rule lists behind <see cref="NamingPreset"/>.</summary>
    public static class NamingRulePresets
    {
        const string Images = ".png, .jpg, .jpeg, .tga, .psd, .tif, .tiff, .exr, .bmp";
        const string Models = ".fbx, .obj, .blend, .dae, .gltf, .glb";
        const string Audio  = ".wav, .mp3, .ogg, .aiff, .aif, .flac";

        /// <summary>One-line description shown next to each preset in the picker.</summary>
        public static string Describe(NamingPreset preset)
        {
            switch (preset)
            {
                case NamingPreset.UnityStandard:
                    return "PascalCase everywhere, no prefixes. The safest place to start on an existing project.";
                case NamingPreset.UnrealStyle:
                    return "T_Texture, M_Material, SM_Mesh, BP_Prefab. Great when your art team came from Unreal.";
                case NamingPreset.Mobile2D:
                    return "Sprite and UI prefixes, enforced art folders, short paths. Built for 2D mobile.";
                case NamingPreset.Minimal:
                    return "No spaces, no 'final_copy', no duplicate names. Nothing else. Impossible to argue with.";
                case NamingPreset.StrictStudio:
                    return "Every check on, every finding an error. Use once the team already agrees.";
                default:
                    return string.Empty;
            }
        }

        /// <summary>Overwrites a rule set with the chosen preset. The caller handles Undo and saving.</summary>
        public static void Apply(NamingRuleSet target, NamingPreset preset)
        {
            if (target == null) return;

            target.rules = Build(preset);
            target.version = NamingRuleSet.CurrentVersion;
            target.lastAppliedPreset = preset.ToString();

            // Project-wide switches differ per preset too.
            switch (preset)
            {
                case NamingPreset.Minimal:
                    target.checkDuplicateNames = true;
                    target.duplicateScope = DuplicateScope.NameAndExtension;
                    target.duplicateSeverity = NamingSeverity.Warning;
                    target.checkLooseRootAssets = false;
                    target.checkEmptyFolders = false;
                    target.checkRedundantTokens = true;
                    target.checkFolderNaming = false;
                    target.checkNumberPadding = false;
                    target.checkPathLength = false;
                    target.illegalCharacterSeverity = NamingSeverity.Error;
                    target.ciFailOn = NamingSeverity.Error;
                    break;

                case NamingPreset.Mobile2D:
                    target.checkDuplicateNames = true;
                    target.checkLooseRootAssets = true;
                    target.checkEmptyFolders = true;
                    target.checkRedundantTokens = true;
                    target.checkFolderNaming = true;
                    target.folderConvention = NamingConvention.PascalCase;
                    target.checkNumberPadding = true;
                    target.numberPaddingWidth = 2;
                    target.checkPathLength = true;
                    target.maxPathLength = 140;
                    target.ciFailOn = NamingSeverity.Error;
                    break;

                case NamingPreset.StrictStudio:
                    target.checkDuplicateNames = true;
                    target.duplicateScope = DuplicateScope.NameOnly;
                    target.duplicateSeverity = NamingSeverity.Error;
                    target.checkLooseRootAssets = true;
                    target.looseRootSeverity = NamingSeverity.Error;
                    target.checkEmptyFolders = true;
                    target.emptyFolderSeverity = NamingSeverity.Warning;
                    target.checkRedundantTokens = true;
                    target.redundantTokenSeverity = NamingSeverity.Error;
                    target.checkFolderNaming = true;
                    target.folderConvention = NamingConvention.PascalCase;
                    target.folderNamingSeverity = NamingSeverity.Warning;
                    target.checkNumberPadding = true;
                    target.checkPathLength = true;
                    target.maxPathLength = 150;
                    target.pathLengthSeverity = NamingSeverity.Error;
                    target.illegalCharacterSeverity = NamingSeverity.Error;
                    target.ciFailOn = NamingSeverity.Error;
                    break;

                default:
                    target.checkDuplicateNames = true;
                    target.checkLooseRootAssets = true;
                    target.checkEmptyFolders = true;
                    target.checkRedundantTokens = true;
                    target.checkFolderNaming = false;
                    target.checkNumberPadding = false;
                    target.checkPathLength = true;
                    target.ciFailOn = NamingSeverity.Error;
                    break;
            }
        }

        /// <summary>Just the rule list for a preset, without touching project-wide switches.</summary>
        public static List<NamingRule> Build(NamingPreset preset)
        {
            switch (preset)
            {
                case NamingPreset.UnrealStyle:  return UnrealStyle();
                case NamingPreset.Mobile2D:     return Mobile2D();
                case NamingPreset.Minimal:      return Minimal();
                case NamingPreset.StrictStudio: return StrictStudio();
                default:                        return UnityStandard();
            }
        }

        // ================================================================ presets

        static List<NamingRule> UnityStandard()
        {
            return new List<NamingRule>
            {
                Rule("Scripts",           ".cs",     NamingConvention.PascalCase, "", NamingSeverity.Error,
                     "A C# file name must match the class inside it or Unity cannot attach the component."),
                Rule("Scenes",            ".unity",  NamingConvention.PascalCase),
                Rule("Prefabs",           ".prefab", NamingConvention.PascalCase),
                Rule("Materials",         ".mat",    NamingConvention.PascalCase),
                Rule("Textures",          Images,    NamingConvention.PascalCase),
                Rule("Models",            Models,    NamingConvention.PascalCase),
                Rule("Audio",             Audio,     NamingConvention.PascalCase),
                Rule("Animation Clips",   ".anim",   NamingConvention.PascalCase),
                Rule("Animator Controllers", ".controller, .overridecontroller", NamingConvention.PascalCase),
                Rule("Shaders",           ".shader, .shadergraph, .shadersubgraph", NamingConvention.PascalCase),
                Rule("ScriptableObjects", ".asset",  NamingConvention.PascalCase)
            };
        }

        static List<NamingRule> UnrealStyle()
        {
            var rules = new List<NamingRule>
            {
                Prefixed("Textures",          Images,    "T_",   NamingConvention.PascalSnakeCase),
                Prefixed("Materials",         ".mat",    "M_, MI_", NamingConvention.PascalSnakeCase),
                Prefixed("Static Meshes",     Models,    "SM_",  NamingConvention.PascalSnakeCase),
                Prefixed("Prefabs",           ".prefab", "BP_",  NamingConvention.PascalSnakeCase),
                Prefixed("Animation Clips",   ".anim",   "A_",   NamingConvention.PascalSnakeCase),
                Prefixed("Animator Controllers", ".controller, .overridecontroller", "AC_", NamingConvention.PascalSnakeCase),
                Prefixed("Audio",             Audio,     "S_, SFX_, MUS_", NamingConvention.PascalSnakeCase),
                Prefixed("Shaders",           ".shader, .shadergraph, .shadersubgraph", "SH_", NamingConvention.PascalSnakeCase),
                Prefixed("Data Assets",       ".asset",  "DA_",  NamingConvention.PascalSnakeCase),
                Rule("Scripts",               ".cs",     NamingConvention.PascalCase, "", NamingSeverity.Error,
                     "Scripts keep plain PascalCase: the file name has to match the class name."),
                Rule("Scenes",                ".unity",  NamingConvention.PascalSnakeCase, "L_")
            };
            return rules;
        }

        static List<NamingRule> Mobile2D()
        {
            var sprites = Prefixed("Sprites", Images, "SPR_", NamingConvention.PascalSnakeCase);
            sprites.allowedFolders = new[] { "Assets/**/Art/Sprites/**", "Assets/**/Art/Characters/**" };
            sprites.preferredFolder = "Assets/Art/Sprites";
            sprites.note = "Everything the camera sees lives under Art. Atlas packing keys follow the folder.";

            var ui = Prefixed("UI Sprites", Images, "UI_", NamingConvention.PascalSnakeCase);
            ui.pathFilters = new[] { "Assets/**/UI/**" };
            ui.allowedFolders = new[] { "Assets/**/Art/UI/**" };
            ui.preferredFolder = "Assets/Art/UI";

            var sfx = Prefixed("Sound Effects", Audio, "SFX_", NamingConvention.PascalSnakeCase);
            sfx.pathFilters = new[] { "Assets/**/Audio/SFX/**" };

            var music = Prefixed("Music", Audio, "MUS_", NamingConvention.PascalSnakeCase);
            music.pathFilters = new[] { "Assets/**/Audio/Music/**" };

            return new List<NamingRule>
            {
                ui, sprites, sfx, music,
                Prefixed("Materials", ".mat", "M_", NamingConvention.PascalSnakeCase),
                Rule("Prefabs",  ".prefab", NamingConvention.PascalCase),
                Rule("Scenes",   ".unity",  NamingConvention.PascalCase),
                Rule("Scripts",  ".cs",     NamingConvention.PascalCase, "", NamingSeverity.Error),
                Rule("Configs",  ".asset",  NamingConvention.PascalCase, "SO_")
            };
        }

        static List<NamingRule> Minimal()
        {
            var everything = new NamingRule
            {
                displayName = "Everything",
                convention = NamingConvention.Any,
                severity = NamingSeverity.Info,
                minLength = 2,
                maxLength = 96,
                allowAutoFix = true,
                note = "The Minimal preset relies on the project-wide checks: no spaces, no junk words, no duplicates."
            };
            return new List<NamingRule> { everything };
        }

        static List<NamingRule> StrictStudio()
        {
            var rules = UnityStandard();
            foreach (var rule in rules)
            {
                rule.severity = NamingSeverity.Error;
                rule.maxLength = 48;
            }

            var textures = Find(rules, "Textures");
            if (textures != null)
            {
                textures.requiredPrefix = "T_";
                textures.convention = NamingConvention.PascalSnakeCase;
                textures.allowedFolders = new[] { "Assets/**/Art/**" };
                textures.preferredFolder = "Assets/Art/Textures";
            }

            var prefabs = Find(rules, "Prefabs");
            if (prefabs != null)
            {
                prefabs.allowedFolders = new[] { "Assets/**/Prefabs/**" };
                prefabs.preferredFolder = "Assets/Prefabs";
            }

            var scripts = Find(rules, "Scripts");
            if (scripts != null)
            {
                scripts.allowedFolders = new[] { "Assets/**/Scripts/**", "Assets/**/Editor/**" };
                scripts.allowAutoFix = false;   // renaming a script without renaming the class breaks the build
                scripts.note = "Auto-fix is off for scripts on purpose: the class inside has to be renamed too.";
            }

            return rules;
        }

        // ================================================================ builders

        static NamingRule Rule(string name, string extensions, NamingConvention convention,
                               string prefix = "", NamingSeverity severity = NamingSeverity.Warning,
                               string note = "")
        {
            return new NamingRule
            {
                displayName = name,
                extensions = extensions,
                convention = convention,
                requiredPrefix = prefix,
                severity = severity,
                minLength = 2,
                maxLength = 64,
                allowAutoFix = true,
                note = note
            };
        }

        static NamingRule Prefixed(string name, string extensions, string prefix, NamingConvention convention)
        {
            var rule = Rule(name, extensions, convention, prefix);
            rule.note = "Prefix " + prefix.Split(',')[0].Trim() +
                        " makes the Project window search box do the filtering for you.";
            return rule;
        }

        static NamingRule Find(List<NamingRule> rules, string displayName)
        {
            foreach (var rule in rules)
            {
                if (rule.displayName == displayName) return rule;
            }
            return null;
        }
    }
}
