using System;
using System.IO;
using MCPForUnity.Editor.Security;
using MCPForUnity.Editor.Services.AssetGen;
using MCPForUnity.Editor.Services.AssetGen.Http;
using MCPForUnity.Editor.Services.AssetGen.Providers;
using MCPForUnity.Editor.Tools.AssetGen;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using UnityEngine;

namespace MCPForUnityTests.Editor.AssetGen
{
    public class GenerateImageTests
    {
        private const string TestFolder = "Assets/Generated/__assetgen_imgtest";
        private string _dir;
        private EncryptedFileKeyStore _store;

        [SetUp]
        public void SetUp()
        {
            AssetGenJobManager.ResetForTests();
            Environment.SetEnvironmentVariable("MCPFORUNITY_FAL_API_KEY", null);
            Environment.SetEnvironmentVariable("MCPFORUNITY_OPENROUTER_API_KEY", null);
            _dir = Path.Combine(Path.GetTempPath(), "mcp_imghandler_" + Guid.NewGuid().ToString("N"));
            _store = new EncryptedFileKeyStore(_dir);
            SecureKeyStore.OverrideForTests(_store);
            AssetGenJobManager.TransportOverrideForTests = new FakeHttpTransport();
        }

        [TearDown]
        public void TearDown()
        {
            AssetGenJobManager.ResetForTests();
            SecureKeyStore.ResetForTests();
            try { if (Directory.Exists(_dir)) Directory.Delete(_dir, true); } catch { }
            try
            {
                string dp = Application.dataPath.Replace('\\', '/');
                string abs = Path.Combine(dp.Substring(0, dp.Length - "Assets".Length), TestFolder);
                if (Directory.Exists(abs)) Directory.Delete(abs, true);
                if (File.Exists(abs + ".meta")) File.Delete(abs + ".meta");
            }
            catch { }
        }

        private static JObject Call(JObject p)
            => JObject.Parse(JsonConvert.SerializeObject(GenerateImage.HandleCommand(p)));

        private static string ProjectRoot()
        {
            string dp = Application.dataPath.Replace('\\', '/');
            return dp.Substring(0, dp.Length - "Assets".Length);
        }

        private static string WriteProjectFile(string rel, byte[] bytes)
        {
            string abs = Path.Combine(ProjectRoot(), rel).Replace('\\', '/');
            Directory.CreateDirectory(Path.GetDirectoryName(abs));
            File.WriteAllBytes(abs, bytes);
            return rel;
        }

        [Test]
        public void Generate_WithKey_ReturnsPendingJobId()
        {
            _store.Set("fal", "falkey");
            JObject gen = Call(new JObject { ["action"] = "generate", ["provider"] = "fal", ["mode"] = "text", ["prompt"] = "a cat" });
            Assert.AreEqual("pending", (string)gen["_mcp_status"]);
            Assert.IsFalse(string.IsNullOrEmpty((string)gen["data"]["job_id"]));
        }

        [Test]
        public void Generate_NoKey_ReturnsError()
        {
            JObject resp = Call(new JObject { ["action"] = "generate", ["provider"] = "fal", ["mode"] = "text", ["prompt"] = "a cat" });
            Assert.AreEqual(false, (bool)resp["success"]);
            StringAssert.Contains("No API key", (string)resp["error"]);
        }

        [Test]
        public void Generate_ImageMode_MissingFile_ReturnsError()
        {
            _store.Set("fal", "falkey");
            JObject resp = Call(new JObject { ["action"] = "generate", ["provider"] = "fal", ["mode"] = "image", ["imagePath"] = "Assets/does_not_exist_zzz.png" });
            Assert.AreEqual(false, (bool)resp["success"]);
            StringAssert.Contains("not found", ((string)resp["error"]).ToLowerInvariant());
        }

        [Test]
        public void Generate_ImageMode_PathOutsideAssets_ReturnsError()
        {
            _store.Set("fal", "falkey");
            string tmp = Path.Combine(Path.GetTempPath(), "mcp_imgin_" + Guid.NewGuid().ToString("N") + ".png");
            File.WriteAllBytes(tmp, new byte[] { 137, 80, 78, 71 });
            try
            {
                JObject resp = Call(new JObject { ["action"] = "generate", ["provider"] = "fal", ["mode"] = "image", ["imagePath"] = tmp, ["prompt"] = "edit it" });
                Assert.AreEqual(false, (bool)resp["success"]);
                StringAssert.Contains("Assets", (string)resp["error"]);
            }
            finally { try { File.Delete(tmp); } catch { } }
        }

        [Test]
        public void Generate_ImageMode_ProjectLocalPath_Accepted_ReturnsPending()
        {
            _store.Set("fal", "falkey");
            string rel = WriteProjectFile(TestFolder + "/ref.png", new byte[] { 137, 80, 78, 71 });

            JObject gen = Call(new JObject { ["action"] = "generate", ["provider"] = "fal", ["mode"] = "image", ["imagePath"] = rel, ["prompt"] = "edit it" });

            Assert.AreEqual("pending", (string)gen["_mcp_status"]);
        }

        [Test]
        public void Generate_ImageMode_UnsupportedExtension_ReturnsError()
        {
            _store.Set("fal", "falkey");
            string rel = WriteProjectFile(TestFolder + "/bad.tga", new byte[] { 0, 0, 2 });

            JObject resp = Call(new JObject { ["action"] = "generate", ["provider"] = "fal", ["mode"] = "image", ["imagePath"] = rel, ["prompt"] = "x" });

            Assert.AreEqual(false, (bool)resp["success"]);
            StringAssert.Contains("Unsupported", (string)resp["error"]);
        }

        [Test]
        public void ListProviders_ImageOnly()
        {
            JObject resp = Call(new JObject { ["action"] = "list_providers" });
            Assert.AreEqual(true, (bool)resp["success"]);
            string s = resp.ToString();
            StringAssert.Contains("fal", s);
            StringAssert.Contains("openrouter", s);
            StringAssert.DoesNotContain("tripo", s); // model providers excluded
        }

        [Test]
        public void Generate_UnknownProvider_ReturnsError()
        {
            _store.Set("bogus", "k");
            JObject resp = Call(new JObject { ["action"] = "generate", ["provider"] = "bogus", ["mode"] = "text", ["prompt"] = "a cat" });
            Assert.AreEqual(false, (bool)resp["success"]);
        }

        [Test]
        public void OpenRouterInline_EndToEnd_ReachesDone()
        {
            _store.Set("openrouter", "orkey");
            byte[] png = { 137, 80, 78, 71, 13, 10, 26, 10 }; // PNG magic; bytes only need to be written, not validated
            string b64 = Convert.ToBase64String(png);
            AssetGenJobManager.TransportOverrideForTests = new FakeHttpTransport
            {
                Handler = spec => new HttpResult
                {
                    Status = 200,
                    IsSuccess = true,
                    Text = "{\"choices\":[{\"message\":{\"images\":[{\"image_url\":{\"url\":\"data:image/png;base64," + b64 + "\"}}]}}]}"
                }
            };
            AssetGenJobManager.PollIntervalSeconds = 0;
            AssetGenJobManager.ImportOverrideForTests = (job, path) => { job.AssetPath = path; return job; };

            var req = new ImageGenRequest { Provider = "openrouter", Mode = "text", Prompt = "a cat", Name = "imgtest", OutputFolder = TestFolder };
            AssetGenJob job = AssetGenJobManager.StartImageGeneration(req);

            int guard = 0;
            while (!AssetGenJobManager.TryAdvanceForTests(job.JobId) && guard++ < 50) { }
            Assert.Less(guard, 50);
            Assert.AreEqual(AssetGenJobState.Done, job.State);
            StringAssert.EndsWith("imgtest.png", job.AssetPath);
        }
    }
}
