// Assets/_Project/Scripts/Editor/Levels/LevelAuthoringEditor.cs
using UnityEditor;
using UnityEngine;
using TD.Core;

namespace TD.Levels.Editor
{
    /// <summary>
    /// Custom inspector for <see cref="LevelAuthoring"/>. Provides:
    ///   - Standard property fields (via DrawDefaultInspector)
    ///   - "Bake LevelData" button
    ///   - "Sync Status" indicator: shows whether the scene matches its baked data
    ///   - "Last Bake Status" panel (outcome, timestamp, warnings, hash, grid)
    /// </summary>
    /// <remarks>
    /// The Refresh Thumbnail button is intentionally not present in this version.
    /// Refreshing a thumbnail without re-baking would require duplicating the
    /// bake's volume-discovery and rasterization phases just to compute the map
    /// bounds the thumbnail camera frames; the maintenance hazard outweighs the
    /// convenience for now. Revisit when authoring real maps reveals it as a
    /// real friction point.
    /// </remarks>
    [CustomEditor(typeof(LevelAuthoring))]
    public class LevelAuthoringEditor : UnityEditor.Editor
    {
        public override void OnInspectorGUI()
        {
            // Standard fields — preserve all the [Tooltip], [Header], etc. authored on the type.
            DrawDefaultInspector();

            EditorGUILayout.Space(8);
            DrawBakeSection();

            EditorGUILayout.Space(8);
            DrawSyncStatusSection();

            EditorGUILayout.Space(8);
            DrawLastBakeStatusSection();
        }

        // ---------- Bake button ----------

        private void DrawBakeSection()
        {
            var auth = (LevelAuthoring)target;

            EditorGUILayout.LabelField("Bake", EditorStyles.boldLabel);

            // Disable the button when targetAsset is unset — the bake's first hard error would be
            // P2-13 anyway, so save the user the click.
            using (new EditorGUI.DisabledScope(auth.targetAsset == null))
            {
                if (GUILayout.Button("Bake LevelData", GUILayout.Height(28)))
                {
                    // The pipeline logs its own success/failure messages and report.
                    LevelBakePipeline.Bake(auth);
                    // Force a repaint so Sync Status and Last Bake Status update immediately.
                    Repaint();
                }
            }

            if (auth.targetAsset == null)
            {
                EditorGUILayout.HelpBox(
                    "Assign a LevelData ScriptableObject to Target Asset before baking.",
                    MessageType.Info);
            }
        }

        // ---------- Sync status indicator (new this session) ----------

        private void DrawSyncStatusSection()
        {
            var auth = (LevelAuthoring)target;

            EditorGUILayout.LabelField("Sync Status", EditorStyles.boldLabel);

            // Without a target asset there's nothing to compare against.
            if (auth.targetAsset == null)
            {
                EditorGUILayout.HelpBox("No target asset assigned.", MessageType.None);
                return;
            }

            // No baked hash means the asset has never been baked; the comparison
            // would be meaningless.
            string bakedHash = auth.targetAsset.AuthoringHash;
            if (string.IsNullOrEmpty(bakedHash))
            {
                EditorGUILayout.HelpBox(
                    "Never baked. Click Bake LevelData to generate baked data.",
                    MessageType.None);
                return;
            }

            // Recompute the scene's authoring hash on every OnInspectorGUI call.
            // Unity throttles inspector repaints on idle frames, and the hash
            // is sub-millisecond on the largest planned map (~30 volumes for
            // the 9-player map). If profiling later shows this is a problem,
            // we can cache the hash and invalidate on Undo.undoRedoPerformed +
            // EditorSceneManager.sceneDirtied — but the simple version is
            // probably fine for the project's scale.
            string currentHash = LevelBakePipeline.ComputeAuthoringHash(auth);

            if (currentHash == bakedHash)
            {
                EditorGUILayout.HelpBox("✓ Scene matches baked data.", MessageType.Info);
            }
            else
            {
                EditorGUILayout.HelpBox(
                    "⚠ Scene differs from baked data — bake recommended.",
                    MessageType.Warning);

                // Showing the first few chars of each hash makes it obvious
                // when "out of sync" is real vs. a glitch. Useful for debugging
                // the hash function itself if it ever misbehaves.
                EditorGUILayout.LabelField("  Scene hash:", ShortHash(currentHash));
                EditorGUILayout.LabelField("  Baked hash:", ShortHash(bakedHash));
            }
        }

        // ---------- Last bake status (existing, mildly tidied) ----------

        private void DrawLastBakeStatusSection()
        {
            var auth = (LevelAuthoring)target;

            EditorGUILayout.LabelField("Last Bake Status", EditorStyles.boldLabel);

            // Transient failure flag overrides the asset's recorded outcome — failed bakes don't
            // modify the asset, so the asset still shows the prior successful bake. Surface the
            // failure separately so the user knows the current scene's last attempt failed.
            if (LevelAuthoringEditorState.HasUncommittedFailure)
            {
                EditorGUILayout.HelpBox(
                    "Last bake attempt FAILED. The target asset still reflects the previous successful bake. " +
                    "See the console for the error report.",
                    MessageType.Error);
            }

            if (auth.targetAsset == null)
            {
                EditorGUILayout.LabelField("(no target asset)");
                return;
            }

            var data = auth.targetAsset;
            if (string.IsNullOrEmpty(data.LastBakeTimestamp))
            {
                EditorGUILayout.LabelField("Never baked.");
                return;
            }

            EditorGUILayout.LabelField("Outcome:", data.LastBakeOutcome.ToString());
            EditorGUILayout.LabelField("Timestamp (UTC):", data.LastBakeTimestamp);
            EditorGUILayout.LabelField("Warnings:", data.LastBakeWarningCount.ToString());

            if (data.GridSize.x > 0 && data.GridSize.y > 0)
            {
                EditorGUILayout.LabelField("Grid size:", $"{data.GridSize.x} × {data.GridSize.y}");
                EditorGUILayout.LabelField("Grid origin:", data.GridOriginTile.ToString());
            }

            if (!string.IsNullOrEmpty(data.AuthoringHash))
            {
                EditorGUILayout.LabelField("Hash:", ShortHash(data.AuthoringHash));
            }
        }

        // ---------- Helpers ----------

        // First 8 chars of the SHA256 hex with an ellipsis. Plenty for human
        // comparison; the full hash is far too long for the inspector.
        private static string ShortHash(string hash)
        {
            if (string.IsNullOrEmpty(hash)) return "";
            return (hash.Length > 8 ? hash.Substring(0, 8) : hash) + "…";
        }
    }
}
