using JetBrains.Annotations;
using LLM.Editor.Helper;
using UnityEditor;
using UnityEngine;

namespace LLM.Editor.Commands
{
    [System.Serializable]
    public class CreatePrefabParams
    {
        public string sourceObjectQuery;
        public string defaultName;
    }

    [UsedImplicitly]
    public class CreatePrefabCommand : ICommand
    {
        private readonly CreatePrefabParams _params;

        public CreatePrefabCommand(string jsonParams)
        {
            _params = jsonParams?.FromJson<CreatePrefabParams>();
        }

        public CommandOutcome Execute(Data.CommandContext context)
        {
            // Step 1: Find the source asset.
            var guids = AssetDatabase.FindAssets(_params.sourceObjectQuery);
            if (guids.Length == 0)
            {
                Debug.LogError($"[CreatePrefabCommand] No asset found for query: '{_params.sourceObjectQuery}'");
                var choice = TryGetAnyModelFromUserChoice(_params.sourceObjectQuery, out var objPath);
                if (!choice) return CommandOutcome.Error;
                var relativeObjPath = "Assets" + objPath[Application.dataPath.Length..];
                guids = new [] { AssetDatabase.AssetPathToGUID(relativeObjPath) };
            }
            
            var assetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
            var sourceAsset = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
            
            // Step 2: Ask the user for a save path.
            var path = EditorUtility.SaveFilePanel("Save New Prefab", "Assets/", _params.defaultName, "prefab");
            if (string.IsNullOrEmpty(path))
            {
                Debug.LogWarning("[CreatePrefabCommand] User cancelled save dialog. Aborting.");
                // We could clear the command queue here if we want cancellation to stop everything.
                return CommandOutcome.Error;
            }
            
            // We need a relative path for AssetDatabase
            var prefabPath = "Assets" + path[Application.dataPath.Length..];

            // Step 3: Create the prefab.
            var instance = (GameObject)PrefabUtility.InstantiatePrefab(sourceAsset);
            PrefabUtility.SaveAsPrefabAsset(instance, prefabPath);
            Object.DestroyImmediate(instance);

            // Step 4: Store the chosen path in the context for the next commands.
            context.CurrentSubject = prefabPath;
            Debug.Log($"[CreatePrefabCommand] Created prefab at '{prefabPath}' from source '{assetPath}'.");
            return CommandOutcome.Success;
        }

        private static bool TryGetAnyModelFromUserChoice(string sourceObjectQuery, out string prefabPath)
        {
            const string message = "Do you want to select a model to create a prefab from? Choosing 'no' will fail to execute forthcoming commands as well.";
            var result = EditorUtility.DisplayDialog($"{sourceObjectQuery} is not found", message, "OK", "No");
            if (!result)
            {
                prefabPath = null;
                return false;
            }

            prefabPath = EditorUtility.OpenFilePanel("Choose the model to create prefab from", "", "obj,fbx,stl,gltf,glb,usdz,dae");
            if (string.IsNullOrEmpty(prefabPath))
            {
                prefabPath = null;
                return false;
            }
            
            Debug.Log($"[CreatePrefabCommand] User selected '{prefabPath}'.");
            
            var isInsideAssets = prefabPath.StartsWith(Application.dataPath);
            if (isInsideAssets) return true;
            prefabPath = null;
            return false;
        }
    }
}