#if UNITY_EDITOR
namespace NiAkaDev.EzMirror.Editor
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using UnityEditor;

    internal static class EzMirrorPackageDefinition
    {
        internal static readonly string RootPath = EzMirrorAssetResolver.GetPackageRootPathNoCreate();
        internal static readonly string RuntimePath = RootPath + "/Runtime";
        internal static readonly string EditorPath = RootPath + "/Editor";
        internal static readonly string EditorTestsPath = EditorPath + "/Tests";
        internal static readonly string ReadmePath = RootPath + "/README.md";
        internal static readonly string TermsPath = RootPath + "/TERMS.md";
        internal static readonly string ChangelogPath = RootPath + "/CHANGELOG.md";
        internal static readonly string PackageJsonPath = RootPath + "/package.json";
        internal static readonly string PrefabPath = RuntimePath + "/Prefabs/EzMirror.prefab";
        internal static readonly string ProgramAssetPath = RuntimePath + "/Scripts/EzMirror.asset";
        internal static readonly string InputProxyProgramAssetPath = RuntimePath + "/Scripts/EzMirrorInputProxy.asset";
        internal static readonly string InteractInputProxyProgramAssetPath = RuntimePath + "/Scripts/EzMirrorInteractInputProxy.asset";
        internal static readonly string UdonSharpAssemblyDefinitionPath = RuntimePath + "/NiAkaDev.EzMirror.asset";
        internal static readonly string ProfileFolderPath = RuntimePath + "/Profiles";
        internal static readonly string GeneratedMaterialFolderPath = RuntimePath + "/Materials/Generated";
        internal static readonly string SwitchAnimatorControllerPath =
            RuntimePath + "/Animations/EzMirrorSwitch.controller";
        internal static readonly string SwitchModelPath = RuntimePath + "/Models/Hari_Switch.fbx";
        // Runtime prefab intentionally references this setup-visibility material.
        // It is shipped to avoid Missing Material on fresh imports.
        internal static readonly string EditorPreviewMaterialPath = GeneratedMaterialFolderPath + "/EzMirror_EditorPreview.mat";
        internal const string CatalogPath = "package-catalog.yml";
        internal const string BoothDocsPath = "BoothDocs/EzMirror";
        internal const string BoothDescriptionPath = BoothDocsPath + "/0_Description.txt";
        internal const string BoothFeaturesPath = BoothDocsPath + "/1_Features.txt";
        internal const string BoothHowToUsePath = BoothDocsPath + "/2_HowToUse.txt";
        internal const string BoothEnvironmentPath = BoothDocsPath + "/3_TestedEnvironment.txt";
        internal const string BoothTermsPath = BoothDocsPath + "/4_TermsOfService.txt";
        internal const string BoothChangelogPath = BoothDocsPath + "/5_Change Log.txt";
        internal const string ReleaseChecklistPath = "docs/ezmirror-release-checklist-ja.md";

        private static readonly string[] UnityPackageIncludedPaths =
        {
            RuntimePath,
            EditorPath,
            ReadmePath,
            TermsPath,
            ChangelogPath,
        };

        private static readonly string[] UnityPackageExcludedPaths =
        {
            EditorTestsPath,
        };

        private static readonly string[] UnityPackageExcludedPatterns =
        {
            EditorTestsPath,
            RootPath + "/Runtime/Materials/Generated/*GeneratedPreview*",
            RootPath + "/Runtime/Materials/Generated/*_Preview*",
            BoothDocsPath,
            CatalogPath,
            ReleaseChecklistPath,
            "**/Tests",
            "**/tmp",
            "**/temp",
            "**/bak",
            "**/backup",
        };

        private static readonly string[] RequiredAssetPaths =
        {
            RuntimePath,
            EditorPath,
            ReadmePath,
            TermsPath,
            ChangelogPath,
            PackageJsonPath,
            PrefabPath,
            UdonSharpAssemblyDefinitionPath,
            ProgramAssetPath,
            InputProxyProgramAssetPath,
            InteractInputProxyProgramAssetPath,
            ProfileFolderPath + "/HQMirror.asset",
            ProfileFolderPath + "/TransparentMirror.asset",
            ProfileFolderPath + "/LQMirror.asset",
            RuntimePath + "/Materials/_MirrorParent.mat",
            RuntimePath + "/Materials/DummyMaterial.mat",
            EditorPreviewMaterialPath,
            RuntimePath + "/Materials/Switch_Base.mat",
            RuntimePath + "/Materials/Switch_Button.mat",
            RuntimePath + "/Materials/SwitchLamp.mat",
            RuntimePath + "/Materials/SwitchLamp_Off.mat",
            RuntimePath + "/Materials/SwitchLamp_Ready.mat",
            RuntimePath + "/Materials/SwitchLamp_Visible.mat",
            RuntimePath + "/Materials/TransBG.mat",
            SwitchModelPath,
            SwitchAnimatorControllerPath,
            RuntimePath + "/Shaders/EzMirror.shader",
            RuntimePath + "/Shaders/EzMirrorBackground.shader",
            RuntimePath + "/Icons/EzMirror_Icon_Line_512.png",
        };

        private static readonly string[] VerificationOnlyPaths =
        {
            CatalogPath,
            BoothDescriptionPath,
            BoothFeaturesPath,
            BoothHowToUsePath,
            BoothEnvironmentPath,
            BoothTermsPath,
            BoothChangelogPath,
            ReleaseChecklistPath,
        };

        internal static string[] GetUnityPackageIncludedPaths()
        {
            return Copy(UnityPackageIncludedPaths);
        }

        internal static string[] GetUnityPackageExcludedPaths()
        {
            return BuildConcreteUnityPackageExcludedPaths();
        }

        internal static string[] GetRequiredAssetPaths()
        {
            return Copy(RequiredAssetPaths);
        }

        internal static string[] GetVerificationOnlyPaths()
        {
            return Copy(VerificationOnlyPaths);
        }

        internal static bool IsExcludedPath(string path)
        {
            string normalized = NormalizePath(path);
            if (string.IsNullOrEmpty(normalized))
                return false;

            for (int i = 0; i < UnityPackageExcludedPatterns.Length; i++)
            {
                if (MatchesPattern(normalized, NormalizePath(UnityPackageExcludedPatterns[i])))
                    return true;
            }
            return false;
        }

        private static bool MatchesPattern(string path, string pattern)
        {
            if (string.IsNullOrEmpty(pattern))
                return false;
            if (pattern.StartsWith("**/", StringComparison.Ordinal))
                return path.IndexOf(pattern.Substring(3), StringComparison.OrdinalIgnoreCase) >= 0;
            if (pattern.EndsWith("/*GeneratedPreview*", StringComparison.Ordinal))
                return path.StartsWith(pattern.Substring(0, pattern.Length - "*GeneratedPreview*".Length), StringComparison.Ordinal)
                    && path.IndexOf("GeneratedPreview", StringComparison.OrdinalIgnoreCase) >= 0;
            if (pattern.EndsWith("/*_Preview*", StringComparison.Ordinal))
                return path.StartsWith(pattern.Substring(0, pattern.Length - "*_Preview*".Length), StringComparison.Ordinal)
                    && path.IndexOf("_Preview", StringComparison.OrdinalIgnoreCase) >= 0;
            return path == pattern || path.StartsWith(pattern + "/", StringComparison.Ordinal);
        }

        private static string[] BuildConcreteUnityPackageExcludedPaths()
        {
            HashSet<string> paths = new HashSet<string>(StringComparer.Ordinal);
            for (int i = 0; i < UnityPackageExcludedPaths.Length; i++)
                AddExistingAssetPath(paths, UnityPackageExcludedPaths[i]);
            AddExistingMatchingPaths(paths, RootPath);

            string[] result = new string[paths.Count];
            paths.CopyTo(result);
            Array.Sort(result, StringComparer.Ordinal);
            return result;
        }

        private static void AddExistingMatchingPaths(HashSet<string> paths, string rootPath)
        {
            if (!Directory.Exists(rootPath))
                return;

            string[] entries = Directory.GetFileSystemEntries(
                rootPath,
                "*",
                SearchOption.AllDirectories
            );
            for (int i = 0; i < entries.Length; i++)
            {
                string path = NormalizePath(entries[i]);
                if (path.EndsWith(".meta", StringComparison.OrdinalIgnoreCase))
                    continue;
                if (path == EditorPreviewMaterialPath)
                    continue;
                if (IsExcludedPath(path))
                    AddExistingAssetPath(paths, path);
            }
        }

        private static void AddExistingAssetPath(HashSet<string> paths, string path)
        {
            string normalized = NormalizePath(path);
            if (string.IsNullOrEmpty(normalized))
                return;
            if (
                !normalized.StartsWith("Assets/", StringComparison.Ordinal)
                && !normalized.StartsWith("Packages/", StringComparison.Ordinal)
            )
                return;
            if (
                AssetDatabase.IsValidFolder(normalized)
                || AssetDatabase.LoadMainAssetAtPath(normalized) != null
            )
            {
                paths.Add(normalized);
            }
        }

        private static string NormalizePath(string path)
        {
            return string.IsNullOrEmpty(path) ? string.Empty : path.Replace('\\', '/').TrimEnd('/');
        }

        private static string[] Copy(string[] source)
        {
            string[] copy = new string[source.Length];
            Array.Copy(source, copy, source.Length);
            return copy;
        }
    }
}
#endif
