using System.Net;
using System.Text;
using System.Text.Json;
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using YMhut.Box.Core.Tools;
using YMhut.Box.WinUI.Services;
using YMhut.Box.WinUI.ViewModels.Tools;

namespace YMhut.Box.WinUI.Views.Tools;

public sealed class HtmlJsPlaygroundToolViewModel(IToolModule module) : AdaptiveToolViewModel(module);

public sealed class HtmlJsPlaygroundToolPage : ToolPageBase
{
    private readonly IToolModule _module;
    private readonly Action? _goBack;
    private readonly WebView2EnvironmentFactory _webViewEnvironmentFactory = AppServices.GetRequiredService<WebView2EnvironmentFactory>();
    private readonly WebView2 _preview = new();
    private readonly TextBox _htmlBox = Editor("HTML", "<main><h1>YMhut HTML/JS</h1><p id=\"out\"></p></main>");
    private readonly TextBox _cssBox = Editor("CSS", "body{font-family:system-ui;margin:24px} h1{color:#0067c0}");
    private readonly TextBox _jsBox = Editor("JavaScript", "const input = await ymhut.input.get();\ndocument.querySelector('#out').textContent = '输入：' + input.text;\nymhut.output.set({ ok: true, input });\nymhut.log.info('rendered');");
    private readonly TextBox _refsBox = Editor("引用 URL（每行一个）", string.Empty);
    private readonly TextBox _inputBox = Editor("输入 JSON", "{\"text\":\"hello\"}");
    private readonly TextBox _logBox = Editor("日志 / 输出", string.Empty, readOnly: true);
    private bool _loaded;

    public HtmlJsPlaygroundToolPage(IToolModule module, Action? goBack = null)
    {
        _module = module;
        _goBack = goBack;
        BindModule(module);
        Background = ModernUi.AppBackground;
        Content = BuildContent();
        Loaded += async (_, _) => await EnsureWebViewAsync();
    }

    private UIElement BuildContent()
    {
        var root = new Grid
        {
            Padding = new Thickness(32, 24, 32, 28),
            RowSpacing = 14
        };
        root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        root.RowDefinitions.Add(new RowDefinition());

        var back = ModernUi.IconButton("\uE72B", "返回工具箱", () => _goBack?.Invoke());
        var run = ModernUi.PillButton("运行", "\uE768", async () => await RunAsync(), primary: true);
        var headerTitle = new StackPanel
        {
            Spacing = 4,
            Children =
            {
                ModernUi.Text(_module.Metadata.Name, 28, FontWeights.SemiBold),
                ModernUi.Text("输入 HTML/CSS/JS 和引用脚本，在隔离 WebView2 中预览，并通过 ymhut Bridge 输出结构化结果。", 14, foreground: ModernUi.TextSecondary, maxLines: 2)
            }
        };
        var header = new Grid { ColumnSpacing = 12 };
        header.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        header.ColumnDefinitions.Add(new ColumnDefinition());
        header.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        header.Children.Add(back);
        Grid.SetColumn(headerTitle, 1);
        header.Children.Add(headerTitle);
        Grid.SetColumn(run, 2);
        header.Children.Add(run);
        root.Children.Add(ModernUi.Card(header, new Thickness(14), radius: 8));

        var body = new Grid { ColumnSpacing = 14 };
        body.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.46, GridUnitType.Star) });
        body.ColumnDefinitions.Add(new ColumnDefinition());
        Grid.SetRow(body, 1);
        root.Children.Add(body);

        var editorTabs = new TabView { IsAddTabButtonVisible = false };
        editorTabs.TabItems.Add(Tab("HTML", _htmlBox));
        editorTabs.TabItems.Add(Tab("CSS", _cssBox));
        editorTabs.TabItems.Add(Tab("JS", _jsBox));
        editorTabs.TabItems.Add(Tab("引用", _refsBox));
        editorTabs.TabItems.Add(Tab("输入", _inputBox));
        editorTabs.TabItems.Add(Tab("日志", _logBox));
        body.Children.Add(ModernUi.Card(editorTabs, new Thickness(8), radius: 8));

        var previewHost = new Grid
        {
            BorderBrush = ModernUi.Stroke,
            BorderThickness = new Thickness(1),
            CornerRadius = new CornerRadius(8),
            Background = ModernUi.Surface
        };
        previewHost.Children.Add(_preview);
        Grid.SetColumn(previewHost, 1);
        body.Children.Add(previewHost);

        body.SizeChanged += (_, e) =>
        {
            var narrow = e.NewSize.Width < 900;
            body.ColumnDefinitions.Clear();
            body.RowDefinitions.Clear();
            body.ColumnDefinitions.Add(new ColumnDefinition());
            if (narrow)
            {
                body.RowDefinitions.Add(new RowDefinition { Height = new GridLength(0.52, GridUnitType.Star) });
                body.RowDefinitions.Add(new RowDefinition());
                Grid.SetColumn(previewHost, 0);
                Grid.SetRow(previewHost, 1);
            }
            else
            {
                body.ColumnDefinitions.Add(new ColumnDefinition());
                Grid.SetColumn(previewHost, 1);
                Grid.SetRow(previewHost, 0);
            }
        };
        return root;
    }

    private static TabViewItem Tab(string title, UIElement content) => new()
    {
        Header = title,
        Content = content
    };

    private static TextBox Editor(string header, string text, bool readOnly = false) => new()
    {
        Header = header,
        Text = text,
        AcceptsReturn = true,
        TextWrapping = TextWrapping.Wrap,
        FontFamily = new Microsoft.UI.Xaml.Media.FontFamily("Cascadia Mono"),
        IsReadOnly = readOnly,
        MinHeight = 420,
        HorizontalAlignment = HorizontalAlignment.Stretch,
        VerticalAlignment = VerticalAlignment.Stretch
    };

    private async Task EnsureWebViewAsync()
    {
        if (_loaded)
        {
            return;
        }

        _loaded = true;
        var environment = await _webViewEnvironmentFactory.CreateAsync("HtmlJsPlayground");
        await _preview.EnsureCoreWebView2Async(environment);
        _preview.CoreWebView2.WebMessageReceived += (_, args) => AppendLog(args.WebMessageAsJson);
        await RunAsync();
    }

    private async Task RunAsync()
    {
        if (_preview.CoreWebView2 is null)
        {
            return;
        }

        _logBox.Text = string.Empty;
        var refs = BuildReferences(_refsBox.Text);
        if (refs.Errors.Count > 0)
        {
            _logBox.Text = string.Join(Environment.NewLine, refs.Errors);
            return;
        }

        var input = NormalizeJsonInput(_inputBox.Text);
        var document = $$"""
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https: data: blob: 'unsafe-inline' 'unsafe-eval'; img-src https: data: blob: file:;">
<style>{{_cssBox.Text}}</style>
{{refs.Tags}}
<script>
(() => {
  const input = {{input}};
  const post = (type, value) => chrome.webview.postMessage({ type, value });
  window.ymhut = {
    input: { get: async () => input, set: async value => post('input.set', value), onInputChanged: () => {} },
    output: { set: value => post('output.set', value), append: value => post('output.append', value), clear: () => post('output.clear', '') },
    log: { info: (message, detail) => post('log.info', { message, detail }), warn: (message, detail) => post('log.warn', { message, detail }), error: (message, detail) => post('log.error', { message, detail }) },
    storage: { get: async () => null, set: async () => true, remove: async () => true, list: async () => ({}) },
    openExternal: url => post('openExternal', url)
  };
  for (const name of ['log','warn','error']) {
    const original = console[name].bind(console);
    console[name] = (...args) => { post('console.' + name, args.map(String).join(' ')); original(...args); };
  }
})();
</script>
</head>
<body>
{{_htmlBox.Text}}
<script type="module">
try {
{{_jsBox.Text}}
} catch (error) {
  ymhut.log.error(error && error.stack ? error.stack : String(error));
}
</script>
</body>
</html>
""";
        _preview.NavigateToString(document);
    }

    private void AppendLog(string json)
    {
        _logBox.Text += json + Environment.NewLine;
    }

    private static string NormalizeJsonInput(string input)
    {
        if (string.IsNullOrWhiteSpace(input))
        {
            return "{}";
        }

        try
        {
            using var document = JsonDocument.Parse(input);
            return document.RootElement.GetRawText();
        }
        catch (JsonException)
        {
            return JsonSerializer.Serialize(new { text = input });
        }
    }

    private static (string Tags, IReadOnlyList<string> Errors) BuildReferences(string refs)
    {
        var tags = new StringBuilder();
        var errors = new List<string>();
        foreach (var raw in refs.Replace("\r\n", "\n").Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
        {
            if (!Uri.TryCreate(raw, UriKind.Absolute, out var uri) ||
                (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps))
            {
                errors.Add($"引用无效或不允许：{raw}");
                continue;
            }

            var safe = WebUtility.HtmlEncode(uri.ToString());
            if (uri.AbsolutePath.EndsWith(".css", StringComparison.OrdinalIgnoreCase))
            {
                tags.AppendLine($"<link rel=\"stylesheet\" href=\"{safe}\">");
            }
            else
            {
                tags.AppendLine($"<script src=\"{safe}\"></script>");
            }
        }

        return (tags.ToString(), errors);
    }
}
