using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Compilation;

namespace Evo.UI.Migration
{
    // Removing the tool once the migration is done. Migrated assets and generated presets never depend on it.
    internal static partial class MigrationEngine
    {
        // Paths inside the tool folder that ship with it, besides its own scripts and Editor/Resources/EvoMigration.* files.
        // Earlier versions shipped README.md, and importing an update over them leaves it behind, so it still counts as the tool's.
        static readonly string[] ToolFiles = { "README.md", "Editor/Evo.UI.Migration.Editor.asmdef", "Editor/MigrationWindow.uss" };

        /// <summary>
        /// The tool folder, if it can be removed: everything in it belongs to the tool and nothing is linked,
        /// so a misplaced install never takes other assets with it. Throws otherwise.
        /// </summary>
        public static string CheckToolRemovable()
        {
            string root = ToolRoot;
            string asmdef = root + "/Editor/Evo.UI.Migration.Editor.asmdef";
            if (string.IsNullOrEmpty(root) || !root.StartsWith("Assets/", StringComparison.Ordinal) || !File.Exists(asmdef))
                throw new InvalidOperationException("The migration tool folder could not be identified.");

            SafePath(asmdef);
            string full = Path.Combine(Root, root);
            if (IsLink(full) || Directory.EnumerateDirectories(full, "*", SearchOption.AllDirectories).Any(IsLink))
                throw new InvalidOperationException("The migration tool folder contains a linked folder, so it is not removed automatically.");

            var foreign = new List<string>();
            foreach (string file in Directory.EnumerateFiles(full, "*", SearchOption.AllDirectories))
            {
                string path = file.Substring(Root.Length + 1).Replace('\\', '/');
                if (!IsToolFile(path, root, asmdef))
                    foreign.Add(path);
            }

            if (foreign.Count > 0)
            {
                string examples = string.Join(", ", foreign.Take(5)) + (foreign.Count > 5 ? $" ({foreign.Count} in total)" : "");
                throw new InvalidOperationException($"The migration tool folder also contains other files, so it was not removed: {examples}.");
            }

            return root;
        }

        static bool IsToolFile(string path, string root, string asmdef)
        {
            string local = path.Substring(root.Length + 1);

            // Unity-ignored folders such as Source~ ship with the tool
            if (path.EndsWith(".meta", StringComparison.Ordinal) || local.Split('/').Any(part => part.EndsWith("~", StringComparison.Ordinal)))
                return true;

            if (ToolFiles.Contains(local) || local.StartsWith("Editor/Resources/EvoMigration.", StringComparison.Ordinal))
                return true;

            return path.EndsWith(".cs", StringComparison.Ordinal)
                && string.Equals(CompilationPipeline.GetAssemblyDefinitionFilePathFromScriptPath(path)?.Replace('\\', '/'), asmdef, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Deletes the tool folder. With data, Library/EvoUIMigration goes too: backups, saved exports, kept v1 files, and analysis runs.
        /// </summary>
        public static void RemoveTool(bool data)
        {
            if (MigrationAutomation.Pending)
                throw new InvalidOperationException("Wait for the automatic migration to finish before removing the tool.");

            string root = CheckToolRemovable();
            string full = Path.Combine(Root, root);
            if (!AssetDatabase.DeleteAsset(root))
                throw new IOException("The migration tool folder could not be removed: " + root);

            if (Directory.Exists(full))
                Directory.Delete(full, true);

            if (File.Exists(full + ".meta"))
                File.Delete(full + ".meta");

            if (data && Directory.Exists(StateDirectory))
                Directory.Delete(StateDirectory, true);
        }
    }
}
