using System;
using System.Collections.Generic;

namespace Evo.UI.Migration
{
    // The export and the analysis request are also read and written by the helper (Source~/Helper/Model.cs),
    // and the other classes are saved in Library, so field names are part of the file formats

    #region Export
    /// <summary>
    /// A migration export: every analyzed asset with its converted content, made in v1 and applied in v2.
    /// </summary>
    [Serializable]
    internal class MigrationManifest
    {
        public int format;
        public string sourceVersion;
        public string targetVersion;
        public string createdUtc;
        public string project;
        public List<MigrationChange> changes = new();

        /// <summary>
        /// v1 sprites, fonts and prefabs that v2 doesn't ship, with their GUIDs and import settings, written to Assets/Evo UI v1 Assets when applying.
        /// Kept prefabs are already converted, and can bring the animation preset copies they need.
        /// </summary>
        public List<MigrationGenerated> kept = new();

        public List<string> notes = new();

        [NonSerialized] Dictionary<string, MigrationChange> changesByGuid;

        /// <summary>
        /// The change for an asset GUID, or null.
        /// </summary>
        public MigrationChange Find(string guid)
        {
            changesByGuid ??= BuildLookup();
            return guid != null && changesByGuid.TryGetValue(guid, out MigrationChange change) ? change : null;
        }

        Dictionary<string, MigrationChange> BuildLookup()
        {
            var lookup = new Dictionary<string, MigrationChange>();
            foreach (MigrationChange change in changes)
                lookup[change.guid] = change;

            return lookup;
        }
    }

    /// <summary>
    /// One asset's conversion. The hashes identify the file before and after migrating, so a change whose hashes match
    /// writes nothing and only carries review notes.
    /// </summary>
    [Serializable]
    internal class MigrationChange
    {
        public string path;
        public string guid;
        public string category;
        public string beforeHash;
        public string afterHash;
        public string content;
        public List<string> details = new();
        public List<string> warnings = new();
        public List<string> requires = new();
        public List<MigrationGenerated> generated = new();

        // Review state, never exported
        [NonSerialized] public bool selected = true;
        [NonSerialized] public ChangeStatus status;
        [NonSerialized] public string resolvedPath;

        public bool HasChanges => beforeHash != afterHash;
        public bool NeedsReview => warnings.Count > 0;

        /// <summary>
        /// Where the asset is now, found by its GUID during a preview, or its path at export time.
        /// </summary>
        public string CurrentPath => string.IsNullOrEmpty(resolvedPath) ? path : resolvedPath;
    }

    /// <summary>
    /// A file the export adds: an animation preset copy that a change needs, written to Assets/Evo UI Migrated Presets,
    /// or a kept v1 asset, written to Assets/Evo UI v1 Assets.
    /// </summary>
    [Serializable]
    internal class MigrationGenerated
    {
        public string path;
        public string guid;
        public string content;
        public string meta;
    }

    /// <summary>
    /// Where an exported change stands in the v2 project, set by MigrationEngine.Preview.
    /// </summary>
    internal enum ChangeStatus
    {
        /// <summary>Not previewed, as in v1.</summary>
        Unknown,
        /// <summary>Nothing to write, only review notes.</summary>
        ReviewOnly,
        Ready,
        Applied,
        /// <summary>The file matches neither the export's source nor its result, so it's never overwritten.</summary>
        Changed,
        Missing,
        OutsideAssets,
        /// <summary>A generated preset copy would overwrite a different file.</summary>
        PresetConflict
    }
    #endregion

    #region Analysis
    /// <summary>
    /// What the analysis reads, as chosen in the window.
    /// </summary>
    [Serializable]
    internal class AnalysisOptions
    {
        public string folder = "Assets";
        public bool prefabs = true;
        public bool scenes = true;
        public bool scripts = true;
        public bool otherAssets = true;

        /// <summary>
        /// Windows code page for scripts that aren't valid UTF-8 and have no BOM, or 0 to skip them.
        /// </summary>
        public int legacyCodePage;

        public bool HasAssetTypes => prefabs || scenes || scripts || otherAssets;
    }

    [Serializable]
    internal class MigrationRequest
    {
        public string root;
        public string catalog;
        public string output;
        public string[] assets;
        public MigrationAssembly[] assemblies;
        public int legacyCodePage;
    }

    [Serializable]
    internal class MigrationAssembly
    {
        public string name;

        /// <summary>
        /// Compiled with the player's symbols for the active build target, which reach code the editor's symbols leave inactive.
        /// </summary>
        public bool player;

        public string[] files;
        public string[] references;
        public string[] defines;
    }
    #endregion

    #region Library state
    [Serializable]
    internal class BackupJournal
    {
        public List<BackupEntry> entries = new();
    }

    [Serializable]
    internal class BackupEntry
    {
        public string path;
        public string backup;
        public string afterHash;
        public bool existed;
    }

    /// <summary>
    /// GUIDs of every file shipped in an Evo UI v1 release (Editor/Resources/EvoMigration.Packages.json).
    /// </summary>
    [Serializable]
    internal class PackageIndex
    {
        public PackageRoot[] roots = { };
        public string[] guids = { };
    }

    [Serializable]
    internal class PackageRoot
    {
        public string name = "";
        public string assembly = "";
    }

    /// <summary>
    /// A pending automatic migration. Paths are project-relative, so the state survives moving the project folder.
    /// </summary>
    [Serializable]
    internal class AutomaticState
    {
        public string stage;
        public string export;
        public string exportHash;
        public string source;
        public string removed;
        public string backup;
        public string summary;
        public bool keepRemoved;
        public List<string> files = new();
        public List<string> folders = new();
        public List<string> kept = new();
        public List<AutomaticScene> scenes = new();
    }

    [Serializable]
    internal class AutomaticScene
    {
        public string path;
        public bool loaded;
        public bool active;
    }
    #endregion

    /// <summary>
    /// Another Evo product that shares Evo/Shared with Evo UI, so it needs its v2 release too.
    /// </summary>
    internal readonly struct RelatedAsset
    {
        public readonly string name;
        public readonly string folder;

        public RelatedAsset(string name, string folder)
        {
            this.name = name;
            this.folder = folder;
        }
    }
}
