using Microsoft.VisualStudio.TestTools.UnitTesting;
using YMhut.Box.Core.Tools;

namespace YMhut.Box.Tests;

[TestClass]
public sealed class ToolCatalogTests
{
    [TestMethod]
    public void DefaultCatalogContainsRequiredNativePages()
    {
        var catalog = new ToolCatalog();

        Assert.IsNotNull(catalog.GetById("safe_browser"));
        Assert.IsNotNull(catalog.GetById("media_player"));
        Assert.IsNotNull(catalog.GetById("text_splitter"));
        Assert.IsNotNull(catalog.GetById("loan_emi_calculator"));
        Assert.IsNotNull(catalog.GetById("number_base_converter"));
        Assert.IsNotNull(catalog.GetById("ymhut_uutool_suite"));
        Assert.IsNotNull(catalog.GetById("html_js_playground"));
        Assert.IsNotNull(catalog.GetById("timezone_abbr_lookup"));
        Assert.IsNotNull(catalog.GetById("percentage_change_calculator"));
        Assert.IsNotNull(catalog.GetById("dev_environment_config"));
        Assert.IsNull(catalog.GetById("mmd_model_studio"));
        foreach (var id in new[]
        {
            "compression_codec",
            "totp_generator",
            "color_contrast_checker",
            "url_redirect_trace",
            "image_metadata_inspector",
            "markdown_preview"
        })
        {
            Assert.IsNotNull(catalog.GetById(id), id);
        }

        Assert.IsGreaterThanOrEqualTo(160, catalog.Modules.Count);
    }

    [TestMethod]
    public void SearchFiltersByQueryAndCategory()
    {
        var catalog = new ToolCatalog();

        var results = catalog.Search("browser", ToolCategory.Network).ToList();

        Assert.HasCount(1, results);
        Assert.AreEqual("safe_browser", results[0].Id);
    }

    [TestMethod]
    public void ToolViewModelStartsWithInlinePreviewHidden()
    {
        var module = new ToolCatalog().GetById("json_formatter")!;

        var viewModel = (ToolModuleViewModel)module.CreateViewModel();

        Assert.IsFalse(viewModel.InlinePreviewLoaded);
    }

    [TestMethod]
    public void EveryCatalogToolHasNativeWinUiPageSpec()
    {
        var catalog = new ToolCatalog();
        var specs = ToolPageSpecCatalog.CreateFor(catalog);

        Assert.HasCount(catalog.Modules.Count, specs);
        foreach (var module in catalog.Modules)
        {
            Assert.IsTrue(specs.ContainsKey(module.Id), module.Id);
            var spec = specs[module.Id];
            Assert.AreEqual(module.Id, spec.ToolId);
            Assert.IsTrue(Enum.IsDefined(spec.Layout), module.Id);
            Assert.IsNotEmpty(spec.ParameterControls, module.Id);
            Assert.IsTrue(Enum.IsDefined(spec.AssistPanelMode), module.Id);
            Assert.IsTrue(Enum.IsDefined(spec.InteractionMode), module.Id);
            Assert.IsTrue(Enum.IsDefined(spec.PageExperience), module.Id);
            Assert.IsTrue(Enum.IsDefined(spec.PageDensity), module.Id);
            Assert.IsTrue(Enum.IsDefined(spec.ResultPriority), module.Id);
            Assert.IsFalse(string.IsNullOrWhiteSpace(spec.DefaultFocusTarget), module.Id);
            if (spec.PageExperience != ToolPageExperienceKind.LivePreview)
            {
                Assert.IsFalse(string.IsNullOrWhiteSpace(spec.PrimaryActionLabel), module.Id);
            }
            if (spec.PrimaryInput != ToolPrimaryInputKind.None)
            {
                Assert.IsNotEmpty(spec.Parameters, module.Id);
            }
            Assert.IsFalse(string.IsNullOrWhiteSpace(spec.EmptyState), module.Id);
            Assert.IsFalse(string.IsNullOrWhiteSpace(spec.ErrorHint), module.Id);
        }
    }

    [TestMethod]
    public void RepresentativeToolSpecsUseGalleryNativeControls()
    {
        var catalog = new ToolCatalog();

        Assert.AreEqual(ToolPrimaryInputKind.MultilineText, ToolPageSpecCatalog.For(catalog.GetById("json_formatter")!).PrimaryInput);
        Assert.AreEqual(ToolResultKind.JsonTree, ToolPageSpecCatalog.For(catalog.GetById("json_formatter")!).Result);
        Assert.AreEqual(ToolPrimaryInputKind.FixedOptions, ToolPageSpecCatalog.For(catalog.GetById("system_tool")!).PrimaryInput);
        Assert.AreEqual(ToolLayoutKind.SystemLauncher, ToolPageSpecCatalog.For(catalog.GetById("system_tool")!).Layout);
        Assert.AreEqual(ToolPrimaryInputKind.None, ToolPageSpecCatalog.For(catalog.GetById("baidu_hot")!).PrimaryInput);
        Assert.IsTrue(ToolPageSpecCatalog.For(catalog.GetById("archive_tool")!).RequiresFilePicker);
        Assert.IsTrue(ToolPageSpecCatalog.For(catalog.GetById("image_metadata_inspector")!).RequiresFilePicker);
        Assert.AreEqual(ToolLayoutKind.CodecWorkbench, ToolPageSpecCatalog.For(catalog.GetById("compression_codec")!).Layout);
        Assert.AreEqual(ToolLayoutKind.SecurityWorkbench, ToolPageSpecCatalog.For(catalog.GetById("totp_generator")!).Layout);
        Assert.AreEqual(ToolLayoutKind.TextWorkbench, ToolPageSpecCatalog.For(catalog.GetById("color_contrast_checker")!).Layout);
        Assert.AreEqual(ToolLayoutKind.QueryDashboard, ToolPageSpecCatalog.For(catalog.GetById("url_redirect_trace")!).Layout);
        Assert.AreEqual(ToolLayoutKind.FormatterWorkbench, ToolPageSpecCatalog.For(catalog.GetById("markdown_preview")!).Layout);
        Assert.AreEqual(ToolLayoutKind.RandomCinema, ToolPageSpecCatalog.For(catalog.GetById("random_cinema")!).Layout);
        Assert.AreEqual(ToolCategory.Image, catalog.GetById("random_cinema")!.Metadata.Category);
    }

    [TestMethod]
    public void ModelStudioToolIsRemovedFromCatalogAndSpecs()
    {
        var catalog = new ToolCatalog();

        Assert.IsNull(catalog.GetById("mmd_model_studio"));
        Assert.IsFalse(ToolPageSpecCatalog.CreateFor(catalog).ContainsKey("mmd_model_studio"));
    }

    [TestMethod]
    public void ToolAssistAndInteractionModesMatchRepresentativeExperience()
    {
        var catalog = new ToolCatalog();

        Assert.AreEqual(ToolAssistPanelMode.Custom, ToolPageSpecCatalog.For(catalog.GetById("color_picker")!).AssistPanelMode);
        Assert.AreEqual(ToolInteractionMode.LivePreview, ToolPageSpecCatalog.For(catalog.GetById("color_picker")!).InteractionMode);
        Assert.AreEqual(ToolPageExperienceKind.LivePreview, ToolPageSpecCatalog.For(catalog.GetById("color_picker")!).PageExperience);
        Assert.AreEqual(ToolAssistPanelMode.Hidden, ToolPageSpecCatalog.For(catalog.GetById("system_info")!).AssistPanelMode);
        Assert.AreEqual(ToolAssistPanelMode.Hidden, ToolPageSpecCatalog.For(catalog.GetById("baidu_hot")!).AssistPanelMode);
        Assert.AreEqual(ToolAssistPanelMode.RulesOnly, ToolPageSpecCatalog.For(catalog.GetById("json_formatter")!).AssistPanelMode);
        Assert.AreEqual(ToolAssistPanelMode.RulesOnly, ToolPageSpecCatalog.For(catalog.GetById("base64_codec")!).AssistPanelMode);
        Assert.AreEqual(ToolAssistPanelMode.SourceOnly, ToolPageSpecCatalog.For(catalog.GetById("weather")!).AssistPanelMode);
        Assert.AreEqual(ToolInteractionMode.ReferenceBrowser, ToolPageSpecCatalog.For(catalog.GetById("mime_lookup")!).InteractionMode);
        Assert.AreEqual(ToolPageExperienceKind.RemoteDashboard, ToolPageSpecCatalog.For(catalog.GetById("baidu_hot")!).PageExperience);
        Assert.AreEqual(ToolPageExperienceKind.FormCalculator, ToolPageSpecCatalog.For(catalog.GetById("bmi_calculator")!).PageExperience);
        Assert.AreEqual(ToolPageExperienceKind.FileWorkflow, ToolPageSpecCatalog.For(catalog.GetById("archive_tool")!).PageExperience);
    }

    [TestMethod]
    public void RuleDrivenToolsHaveRuleSpecsAndSourceOnlyCopyIsSanitized()
    {
        var catalog = new ToolCatalog();
        var specs = ToolPageSpecCatalog.CreateFor(catalog);

        foreach (var spec in specs.Values.Where(spec => spec.AssistPanelMode == ToolAssistPanelMode.RulesOnly))
        {
            Assert.IsTrue(spec.Rules.Count > 0 || spec.RequiresFilePicker || spec.ParameterControls.Contains(ToolParameterControlKind.NumberBox), spec.ToolId);
        }

        foreach (var spec in specs.Values.Where(spec => spec.AssistPanelMode == ToolAssistPanelMode.SourceOnly))
        {
            var uiCopy = $"{spec.EmptyState} {spec.ErrorHint}";
            Assert.IsFalse(uiCopy.Contains("http://", StringComparison.OrdinalIgnoreCase), spec.ToolId);
            Assert.IsFalse(uiCopy.Contains("https://", StringComparison.OrdinalIgnoreCase), spec.ToolId);
            Assert.IsFalse(uiCopy.Contains(".json", StringComparison.OrdinalIgnoreCase), spec.ToolId);
        }
    }

    [TestMethod]
    public void RuleAndParameterSpecsCarryBilingualText()
    {
        var catalog = new ToolCatalog();
        var json = ToolPageSpecCatalog.For(catalog.GetById("json_formatter")!);
        var port = ToolPageSpecCatalog.For(catalog.GetById("port_lookup")!);

        Assert.AreEqual("模式", json.Rules[0].LocalizedLabel?.ForLanguage("zh-CN"));
        Assert.AreEqual("Mode", json.Rules[0].LocalizedLabel?.ForLanguage("en-US"));
        Assert.AreEqual("格式化", json.Rules[0].Options[0].LocalizedLabel?.ForLanguage("zh-CN"));
        Assert.AreEqual("Format", json.Rules[0].Options[0].LocalizedLabel?.ForLanguage("en-US"));
        Assert.AreEqual("来源", port.Rules[0].LocalizedLabel?.ForLanguage("zh-CN"));
        Assert.AreEqual("Source", port.Rules[0].LocalizedLabel?.ForLanguage("en-US"));
        Assert.AreEqual("预设", port.Parameters[0].LocalizedLabel?.ForLanguage("zh-CN"));
        Assert.AreEqual("Preset", port.Parameters[0].LocalizedLabel?.ForLanguage("en-US"));
    }

    [TestMethod]
    public void PageExperienceRulesKeepToolPagesCompact()
    {
        var catalog = new ToolCatalog();

        var color = ToolPageSpecCatalog.For(catalog.GetById("color_picker")!);
        Assert.AreEqual(ToolPageExperienceKind.LivePreview, color.PageExperience);
        Assert.AreEqual(string.Empty, color.PrimaryActionLabel);

        var hot = ToolPageSpecCatalog.For(catalog.GetById("hotboard")!);
        Assert.AreEqual(ToolPageExperienceKind.RemoteDashboard, hot.PageExperience);
        Assert.IsFalse(hot.AutoRunOnOpen);
        Assert.AreEqual("查询", hot.PrimaryActionLabel);
        Assert.IsTrue(hot.ShowInputHeader);

        var bmi = ToolPageSpecCatalog.For(catalog.GetById("bmi_calculator")!);
        Assert.AreEqual(ToolPageExperienceKind.FormCalculator, bmi.PageExperience);
        Assert.AreEqual("计算", bmi.PrimaryActionLabel);
        Assert.AreNotEqual(ToolPrimaryInputKind.MultilineText, bmi.PrimaryInput);

        var json = ToolPageSpecCatalog.For(catalog.GetById("json_formatter")!);
        Assert.AreEqual(ToolPageExperienceKind.OneShotTransform, json.PageExperience);
        Assert.AreEqual("格式化", json.PrimaryActionLabel);
    }

    [TestMethod]
    public void CalculatorToolSpecsUseNativeFormInputsByArity()
    {
        var catalog = new ToolCatalog();

        Assert.AreEqual(ToolPrimaryInputKind.NumberPair, ToolPageSpecCatalog.For(catalog.GetById("bmi_calculator")!).PrimaryInput);
        Assert.AreEqual(ToolPrimaryInputKind.NumberTriple, ToolPageSpecCatalog.For(catalog.GetById("loan_emi_calculator")!).PrimaryInput);
        Assert.AreEqual(ToolPrimaryInputKind.NumberQuad, ToolPageSpecCatalog.For(catalog.GetById("compound_interest_calculator")!).PrimaryInput);
        Assert.AreEqual(ToolPrimaryInputKind.DateRange, ToolPageSpecCatalog.For(catalog.GetById("shelf_life")!).PrimaryInput);
        Assert.IsTrue(ToolPageSpecCatalog.For(catalog.GetById("shelf_life")!).UsesNumberBox);
    }

    [TestMethod]
    public void SpecsCoverEveryStructuredResultKind()
    {
        var catalog = new ToolCatalog();
        var resultKinds = catalog.Modules
            .Select(module => ToolPageSpecCatalog.For(module).Result)
            .Distinct()
            .ToHashSet();

        foreach (ToolResultKind kind in Enum.GetValues<ToolResultKind>())
        {
            Assert.Contains(kind, resultKinds, kind.ToString());
        }
    }

    [TestMethod]
    public async Task ToolExecutorReturnsStructuredDocuments()
    {
        var catalog = new ToolCatalog();
        var samples = new Dictionary<string, string>
        {
            ["json_formatter"] = "{\"name\":\"YMhut Box\"}",
            ["base64_codec"] = "YMhut Box",
            ["bmi_calculator"] = "70 1.75",
            ["text_diff"] = "WinUI\n---\nWinUI 3",
            ["color_picker"] = "#3399ff"
        };

        foreach (var sample in samples)
        {
            var module = catalog.GetById(sample.Key)!;
            var result = await ToolExecutor.ExecuteAsync(module, sample.Value);

            Assert.IsTrue(result.Ok, sample.Key);
            Assert.IsNotNull(result.Document, sample.Key);
            Assert.AreEqual(module.Id, result.Document.ToolId);
            Assert.IsNotEmpty(result.Document.Blocks, sample.Key);
            Assert.AreEqual(result.Output, result.Document.RawText);
        }
    }

    [TestMethod]
    public void EveryResultKindBuildsStructuredDocumentBlocks()
    {
        var catalog = new ToolCatalog();
        var samples = new Dictionary<string, string>
        {
            ["ascii_art"] = "YMhut",
            ["xml_formatter"] = "<root><name>YMhut</name></root>",
            ["json_formatter"] = "{\"name\":\"YMhut\"}",
            ["base64_codec"] = "Encode:\nWU1odXQ=",
            ["regex_tool"] = "match | value\n1 | YMhut",
            ["hotboard"] = "1. YMhut 热点",
            ["cctv_news"] = "1. 新闻标题",
            ["qr_generator"] = "https://example.com/qr.png",
            ["smart_search"] = "Bing - https://www.bing.com/search?q=YMhut",
            ["archive_tool"] = "已创建归档：sample.zip",
            ["text_diff"] = "- old\n+ new",
            ["hash_manifest_verify"] = "OK  hello",
            ["color_picker"] = "HEX: #336699\nRGB: 51, 102, 153",
            ["number_base"] = "DEC: 255\nHEX: 0xFF",
            ["system_tool"] = "OS: Windows\nProcessors: 8",
            ["random_cinema"] = "https://example.com/media.mp4",
            ["port_lookup"] = "数据源：YMhut Reference Data / IANA\n80 - HTTP: 网页服务"
        };

        foreach (ToolResultKind kind in Enum.GetValues<ToolResultKind>())
        {
            var module = catalog.Modules.FirstOrDefault(module => ToolPageSpecCatalog.For(module).Result == kind);
            Assert.IsNotNull(module, kind.ToString());
            var output = samples.TryGetValue(module.Id, out var sample) ? sample : "Key: Value\nItem: Result";

            var document = ToolResultBuilder.FromOutput(module, output, "zh-CN");

            Assert.AreEqual(module.Id, document.ToolId, kind.ToString());
            Assert.AreEqual(kind, document.ResultKind, kind.ToString());
            Assert.IsNotEmpty(document.Blocks, kind.ToString());
            Assert.AreEqual("ok", document.Status, kind.ToString());
        }
    }
}
