using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using CommunityToolkit.WinUI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Windows.UI.ViewManagement.Core;
using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Reader;
using Wino.Mail.WinUI;
using Wino.Mail.WinUI.Extensions;

namespace Wino.Mail.Controls;

public sealed partial class WebViewEditorControl : Control, IDisposable, IEditorCommandTarget
{
    private const string PART_WebView = "WebView";

    private static readonly IReadOnlyList<int> DefaultFontSizes = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36];
    private static readonly IReadOnlyList<EditorColorOption> DefaultTextColors =
    [
        new("Default", string.Empty),
        new("Black", "#000000"),
        new("Gray", "#666666"),
        new("Red", "#c62828"),
        new("Orange", "#ef6c00"),
        new("Yellow", "#f9a825"),
        new("Green", "#2e7d32"),
        new("Blue", "#1565c0"),
        new("Purple", "#6a1b9a")
    ];
    private static readonly IReadOnlyList<EditorColorOption> DefaultHighlightColors =
    [
        new("None", string.Empty),
        new("Yellow", "#fff59d"),
        new("Green", "#c8e6c9"),
        new("Blue", "#bbdefb"),
        new("Pink", "#f8bbd0"),
        new("Orange", "#ffe0b2")
    ];
    private static readonly IReadOnlyList<EditorParagraphStyleOption> DefaultParagraphStyles =
    [
        new("Paragraph", "div"),
        new("Heading 1", "h1"),
        new("Heading 2", "h2"),
        new("Heading 3", "h3"),
        new("Quote", "blockquote"),
        new("Code", "pre")
    ];
    private static readonly IReadOnlyList<string> DefaultLineHeights = ["normal", "1", "1.15", "1.5", "2"];
    private static readonly IReadOnlyList<EditorTextAlignment> DefaultAlignments =
        [EditorTextAlignment.Left, EditorTextAlignment.Center, EditorTextAlignment.Right, EditorTextAlignment.Justify];

    private readonly INativeAppService _nativeAppService = App.Current.Services.GetService<INativeAppService>()!;
    private readonly IFontService _fontService = App.Current.Services.GetService<IFontService>()!;
    private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>()!;

    [GeneratedDependencyProperty]
    public partial bool IsEditorDarkMode { get; set; }
    async partial void OnIsEditorDarkModeChanged(bool newValue)
    {
        UpdateState(GetCurrentStateOrDefault() with { IsDarkMode = newValue });
        await UpdateEditorThemeAsync();
    }

    [GeneratedDependencyProperty]
    public partial bool IsEditorWebViewEditor { get; set; }
    async partial void OnIsEditorWebViewEditorChanged(bool newValue)
    {
        UpdateState(GetCurrentStateOrDefault() with { IsBuiltInToolbarVisible = newValue });
        await ApplyBuiltInToolbarVisibilityAsync();
    }

    private WebView2? _chromium;
    private bool _disposedValue;
    private bool? _lastAppliedDarkTheme;
    private readonly TaskCompletionSource<bool> _domLoadedTask = new();
    private readonly TaskCompletionSource<bool> _editorReadyTask = new();
    private readonly object _editorInitializationLock = new();
    private Task? _editorInitializationTask;

    public event EventHandler<EditorState>? StateChanged;
    public event EventHandler<EditorCapabilities>? CapabilitiesChanged;

    public EditorState CurrentState { get; private set; }
    public EditorCapabilities Capabilities { get; private set; }

    public WebViewEditorControl()
    {
        DefaultStyleKey = typeof(WebViewEditorControl);

        IsEditorDarkMode = WinoApplication.Current.UnderlyingThemeService.IsUnderlyingThemeDark();
        Capabilities = BuildCapabilities(_fontService.GetFonts());
        CurrentState = CreateDefaultState();
    }

    protected override async void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        var newWebView = GetTemplateChild(PART_WebView) as WebView2;
        if (newWebView == null)
        {
            return;
        }

        if (_chromium != null && !ReferenceEquals(_chromium, newWebView))
        {
            DetachChromium(_chromium, closeWebView: false);
        }

        _chromium = newWebView;
        await InitializeWebViewAsync();
    }

    public async Task ExecuteCommandAsync(EditorCommand command)
    {
        ObjectDisposedException.ThrowIf(_disposedValue, this);
        await EnsureEditorReadyAsync();

        if (_chromium == null)
        {
            return;
        }

        switch (command.Kind)
        {
            case EditorCommandKind.ToggleBold:
                await ExecuteEditorCommandAsync("bold");
                break;
            case EditorCommandKind.ToggleItalic:
                await ExecuteEditorCommandAsync("italic");
                break;
            case EditorCommandKind.ToggleUnderline:
                await ExecuteEditorCommandAsync("underline");
                break;
            case EditorCommandKind.ToggleStrikethrough:
                await ExecuteEditorCommandAsync("strikethrough");
                break;
            case EditorCommandKind.ToggleOrderedList:
                await ExecuteEditorCommandAsync("insertorderedlist");
                break;
            case EditorCommandKind.ToggleUnorderedList:
                await ExecuteEditorCommandAsync("insertunorderedlist");
                break;
            case EditorCommandKind.Indent:
                await ExecuteEditorCommandAsync("indent");
                break;
            case EditorCommandKind.Outdent:
                await ExecuteEditorCommandAsync("outdent");
                break;
            case EditorCommandKind.SetAlignment when command.Value is EditorTextAlignment alignment:
                await ExecuteEditorCommandAsync(alignment switch
                {
                    EditorTextAlignment.Left => "justifyleft",
                    EditorTextAlignment.Center => "justifycenter",
                    EditorTextAlignment.Right => "justifyright",
                    EditorTextAlignment.Justify => "justifyfull",
                    _ => "justifyleft"
                });
                break;
            case EditorCommandKind.SetFontFamily when command.Value is string fontFamily:
                await _chromium.ExecuteScriptFunctionSafeAsync("setFontFamily", JsonSerializer.Serialize(fontFamily, BasicTypesJsonContext.Default.String));
                break;
            case EditorCommandKind.SetFontSize when command.Value is int fontSize:
                await _chromium.ExecuteScriptFunctionSafeAsync("setFontSize", JsonSerializer.Serialize(fontSize, BasicTypesJsonContext.Default.Int32));
                break;
            case EditorCommandKind.SetParagraphStyle when command.Value is string paragraphStyle:
                await _chromium.ExecuteScriptFunctionSafeAsync("setParagraphStyle", JsonSerializer.Serialize(paragraphStyle, BasicTypesJsonContext.Default.String));
                break;
            case EditorCommandKind.SetTextColor when command.Value is string textColor:
                await _chromium.ExecuteScriptFunctionSafeAsync("setTextColor", JsonSerializer.Serialize(textColor, BasicTypesJsonContext.Default.String));
                break;
            case EditorCommandKind.SetHighlightColor when command.Value is string highlightColor:
                await _chromium.ExecuteScriptFunctionSafeAsync("setHighlightColor", JsonSerializer.Serialize(highlightColor, BasicTypesJsonContext.Default.String));
                break;
            case EditorCommandKind.SetLineHeight when command.Value is string lineHeight:
                await _chromium.ExecuteScriptFunctionSafeAsync("setLineHeight", JsonSerializer.Serialize(lineHeight, BasicTypesJsonContext.Default.String));
                break;
            case EditorCommandKind.InsertImage:
                await ShowImagePickerAsync();
                return;
            case EditorCommandKind.InsertLink when command.Value is EditorLinkCommandArgs linkArgs:
                await _chromium.ExecuteScriptFunctionSafeAsync("upsertLink", SerializeLinkArgs(linkArgs));
                break;
            case EditorCommandKind.RemoveLink:
                await _chromium.ExecuteScriptFunctionSafeAsync("removeLink");
                break;
            case EditorCommandKind.InsertEmoji:
                await ShowEmojiPickerAsync();
                return;
            case EditorCommandKind.InsertTable when command.Value is EditorTableCommandArgs tableArgs:
                await _chromium.ExecuteScriptFunctionSafeAsync("insertTableHtml", SerializeTableArgs(tableArgs));
                break;
            case EditorCommandKind.ToggleBuiltInToolbar when command.Value is bool isToolbarVisible:
                IsEditorWebViewEditor = isToolbarVisible;
                return;
            case EditorCommandKind.ToggleTheme when command.Value is bool isDarkMode:
                IsEditorDarkMode = isDarkMode;
                return;
            case EditorCommandKind.ToggleSpellCheck when command.Value is bool isSpellCheckEnabled:
                await _chromium.ExecuteScriptFunctionSafeAsync("setSpellCheck", JsonSerializer.Serialize(isSpellCheckEnabled, BasicTypesJsonContext.Default.Boolean));
                break;
            default:
                throw new InvalidOperationException($"Unsupported editor command: {command.Kind}");
        }

        await RefreshStateAsync();
    }

    public async Task EditorIndentAsync() => await ExecuteCommandAsync(EditorCommand.Indent());

    public async Task EditorOutdentAsync() => await ExecuteCommandAsync(EditorCommand.Outdent());

    public void ToggleEditorTheme()
    {
        IsEditorDarkMode = !IsEditorDarkMode;
    }

    public async Task<string?> GetHtmlBodyAsync()
    {
        await EnsureEditorReadyAsync();

        if (_chromium == null)
        {
            return null;
        }

        var editorContent = await _chromium.ExecuteScriptFunctionSafeAsync("GetHTMLContent");
        return JsonSerializer.Deserialize(editorContent, BasicTypesJsonContext.Default.String);
    }

    public async Task ShowImagePickerAsync()
    {
        await EnsureEditorReadyAsync();
        if (_chromium != null)
        {
            await _chromium.ExecuteScriptFunctionSafeAsync("imageInput.click");
        }
    }

    public async void ShowImagePicker()
    {
        await ShowImagePickerAsync();
    }

    public async Task InsertImagesAsync(List<ImageInfo> images)
    {
        await EnsureEditorReadyAsync();
        if (_chromium != null)
        {
            await _chromium.ExecuteScriptFunctionSafeAsync("insertImages", JsonSerializer.Serialize(images, DomainModelsJsonContext.Default.ListImageInfo));
            await RefreshStateAsync();
        }
    }

    public async Task ShowEmojiPickerAsync()
    {
        CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji);
        await FocusEditorAsync(focusControlAsWell: true);
    }

    public async void ShowEmojiPicker()
    {
        await ShowEmojiPickerAsync();
    }

    public WebView2 GetUnderlyingWebView() => _chromium!;

    public async Task RenderHtmlAsync(string htmlBody)
    {
        await EnsureEditorReadyAsync();

        if (_chromium == null)
        {
            return;
        }

        await _chromium.ExecuteScriptFunctionAsync("RenderHTML", JsonSerializer.Serialize(string.IsNullOrEmpty(htmlBody) ? " " : htmlBody, BasicTypesJsonContext.Default.String));
        await RefreshStateAsync();
    }

    public async Task FocusEditorAsync(bool focusControlAsWell)
    {
        await EnsureEditorReadyAsync();

        if (_chromium == null)
        {
            return;
        }

        await _chromium.ExecuteScriptSafeAsync("focusEditor();");

        if (focusControlAsWell)
        {
            _chromium.Focus(FocusState.Keyboard);
            _chromium.Focus(FocusState.Programmatic);
        }
    }

    public async Task UpdateEditorThemeAsync(bool force = false)
    {
        if (_chromium?.CoreWebView2 == null)
        {
            return;
        }

        if (!_editorReadyTask.Task.IsCompleted)
        {
            return;
        }

        var isDark = IsEditorDarkMode;
        if (!force && _lastAppliedDarkTheme == isDark)
        {
            return;
        }

        _lastAppliedDarkTheme = isDark;
        _chromium.CoreWebView2.Profile.PreferredColorScheme = isDark
            ? CoreWebView2PreferredColorScheme.Dark
            : CoreWebView2PreferredColorScheme.Light;

        await _chromium.ExecuteScriptFunctionSafeAsync(isDark ? "SetDarkEditor" : "SetLightEditor");
        UpdateState(CurrentState with { IsDarkMode = isDark });
    }

    private async Task InitializeWebViewAsync()
    {
        if (_chromium == null)
        {
            return;
        }

        var sharedEnvironment = await WebViewExtensions.GetSharedEnvironmentAsync();
        _chromium.CoreWebView2Initialized -= ChromiumInitialized;
        _chromium.CoreWebView2Initialized += ChromiumInitialized;
        await _chromium.EnsureCoreWebView2Async(sharedEnvironment);
    }

    private Task EnsureEditorReadyAsync()
    {
        if (_chromium == null || _disposedValue)
        {
            return Task.CompletedTask;
        }

        return EnsureEditorReadyCoreAsync();
    }

    private async Task EnsureEditorReadyCoreAsync()
    {
        await EnsureEditorInitializedAsync();
        await _editorReadyTask.Task;
    }

    private Task EnsureEditorInitializedAsync()
    {
        lock (_editorInitializationLock)
        {
            _editorInitializationTask ??= EnsureEditorInitializedCoreAsync();
            return _editorInitializationTask;
        }
    }

    private async Task EnsureEditorInitializedCoreAsync()
    {
        if (_chromium == null || _disposedValue)
        {
            _editorReadyTask.TrySetResult(true);
            return;
        }

        await _domLoadedTask.Task;

        if (_chromium == null || _disposedValue)
        {
            _editorReadyTask.TrySetResult(true);
            return;
        }

        var fonts = _fontService.GetFonts();
        await _chromium.ExecuteScriptFunctionAsync(
            "initializeJodit",
            JsonSerializer.Serialize(fonts, BasicTypesJsonContext.Default.ListString),
            JsonSerializer.Serialize(_preferencesService.ComposerFont, BasicTypesJsonContext.Default.String),
            JsonSerializer.Serialize(_preferencesService.ComposerFontSize, BasicTypesJsonContext.Default.Int32),
            JsonSerializer.Serialize(_preferencesService.ReaderFont, BasicTypesJsonContext.Default.String),
            JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32),
            JsonSerializer.Serialize(DefaultTextColors.Select(option => option.Value).Where(value => !string.IsNullOrWhiteSpace(value)).ToList(), BasicTypesJsonContext.Default.ListString),
            JsonSerializer.Serialize(DefaultHighlightColors.Select(option => option.Value).Where(value => !string.IsNullOrWhiteSpace(value)).ToList(), BasicTypesJsonContext.Default.ListString));

        UpdateCapabilities(BuildCapabilities(fonts));
        _editorReadyTask.TrySetResult(true);

        await UpdateEditorThemeAsync(force: true);
        await ApplyBuiltInToolbarVisibilityAsync(force: true);
        await RefreshStateAsync();
    }

    private async Task ExecuteEditorCommandAsync(string command)
    {
        if (_chromium != null)
        {
            await _chromium.ExecuteScriptFunctionSafeAsync("executeEditorCommand", JsonSerializer.Serialize(command, BasicTypesJsonContext.Default.String));
        }
    }

    private async Task ApplyBuiltInToolbarVisibilityAsync(bool force = false)
    {
        if (_chromium == null)
        {
            return;
        }

        if (!_editorReadyTask.Task.IsCompleted)
        {
            return;
        }

        await _chromium.ExecuteScriptFunctionSafeAsync("toggleToolbar", JsonSerializer.Serialize(IsEditorWebViewEditor, BasicTypesJsonContext.Default.Boolean));
        UpdateState(CurrentState with { IsBuiltInToolbarVisible = IsEditorWebViewEditor });
    }

    private async Task RefreshStateAsync()
    {
        if (_chromium == null || !_editorReadyTask.Task.IsCompleted)
        {
            return;
        }

        var stateResult = await _chromium.ExecuteScriptFunctionSafeAsync("getEditorState");
        if (string.IsNullOrWhiteSpace(stateResult))
        {
            return;
        }

        var snapshot = DeserializeStateSnapshot(stateResult);
        if (snapshot != null)
        {
            UpdateState(MapState(snapshot));
        }
    }

    private void ChromiumInitialized(WebView2 sender, CoreWebView2InitializedEventArgs args)
    {
        if (args.Exception != null || _chromium?.CoreWebView2 == null)
        {
            return;
        }

        _ = ConfigureChromiumAsync();
    }

    private async Task ConfigureChromiumAsync()
    {
        if (_chromium?.CoreWebView2 == null)
        {
            return;
        }

        var editorBundlePath = (await _nativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty, StringComparison.OrdinalIgnoreCase);
        _chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.editor", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
        _chromium.CoreWebView2.DOMContentLoaded -= DomLoaded;
        _chromium.CoreWebView2.DOMContentLoaded += DomLoaded;
        _chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived;
        _chromium.CoreWebView2.WebMessageReceived += ScriptMessageReceived;
        _chromium.Source = new Uri("https://app.editor/editor.html");
    }

    private void DomLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args)
    {
        _domLoadedTask.TrySetResult(true);
        _ = EnsureEditorInitializedAsync();
    }

    private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
    {
        using var document = JsonDocument.Parse(args.WebMessageAsJson);
        if (!document.RootElement.TryGetProperty("type", out var typeElement) ||
            !string.Equals(typeElement.GetString(), "state", StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        if (document.RootElement.TryGetProperty("state", out var stateElement))
        {
            var snapshot = DeserializeStateSnapshot(stateElement);
            if (snapshot != null)
            {
                UpdateState(MapState(snapshot));
            }
        }
    }

    private EditorState MapState(EditorStateSnapshot snapshot)
    {
        return new EditorState
        {
            IsBold = snapshot.IsBold,
            IsItalic = snapshot.IsItalic,
            IsUnderline = snapshot.IsUnderline,
            IsStrikethrough = snapshot.IsStrikethrough,
            IsOrderedList = snapshot.IsOrderedList,
            IsUnorderedList = snapshot.IsUnorderedList,
            CanIndent = snapshot.CanIndent,
            CanOutdent = snapshot.CanOutdent,
            HasSelection = snapshot.HasSelection,
            IsDarkMode = IsEditorDarkMode,
            IsBuiltInToolbarVisible = IsEditorWebViewEditor,
            IsSpellCheckEnabled = snapshot.IsSpellCheckEnabled,
            Alignment = ParseAlignment(snapshot.Alignment),
            FontFamily = string.IsNullOrWhiteSpace(snapshot.FontFamily) ? _preferencesService.ComposerFont : snapshot.FontFamily,
            FontSize = snapshot.FontSize ?? _preferencesService.ComposerFontSize,
            ParagraphStyle = string.IsNullOrWhiteSpace(snapshot.ParagraphStyle) ? "div" : snapshot.ParagraphStyle,
            TextColor = snapshot.TextColor ?? string.Empty,
            HighlightColor = snapshot.HighlightColor ?? string.Empty,
            LineHeight = string.IsNullOrWhiteSpace(snapshot.LineHeight) ? "normal" : snapshot.LineHeight,
            LinkUrl = snapshot.LinkUrl,
            SelectedText = snapshot.SelectedText
        };
    }

    private static EditorTextAlignment ParseAlignment(string? alignment)
    {
        return alignment?.ToLowerInvariant() switch
        {
            "center" => EditorTextAlignment.Center,
            "right" => EditorTextAlignment.Right,
            "justify" => EditorTextAlignment.Justify,
            _ => EditorTextAlignment.Left
        };
    }

    private static string SerializeLinkArgs(EditorLinkCommandArgs args)
    {
        var url = JsonSerializer.Serialize(args.Url, BasicTypesJsonContext.Default.String);
        var text = JsonSerializer.Serialize(args.Text, BasicTypesJsonContext.Default.String);
        var openInNewWindow = JsonSerializer.Serialize(args.OpenInNewWindow, BasicTypesJsonContext.Default.Boolean);
        return $"{{\"url\":{url},\"text\":{text},\"openInNewWindow\":{openInNewWindow}}}";
    }

    private static string SerializeTableArgs(EditorTableCommandArgs args)
    {
        var rows = JsonSerializer.Serialize(args.Rows, BasicTypesJsonContext.Default.Int32);
        var columns = JsonSerializer.Serialize(args.Columns, BasicTypesJsonContext.Default.Int32);
        return $"{{\"rows\":{rows},\"columns\":{columns}}}";
    }

    private static EditorStateSnapshot? DeserializeStateSnapshot(string json)
    {
        using var document = JsonDocument.Parse(json);
        return DeserializeStateSnapshot(document.RootElement);
    }

    private static EditorStateSnapshot? DeserializeStateSnapshot(JsonElement element)
    {
        if (element.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
        {
            return null;
        }

        return new EditorStateSnapshot
        {
            IsBold = GetBoolean(element, "bold"),
            IsItalic = GetBoolean(element, "italic"),
            IsUnderline = GetBoolean(element, "underline"),
            IsStrikethrough = GetBoolean(element, "strikethrough"),
            IsOrderedList = GetBoolean(element, "orderedList"),
            IsUnorderedList = GetBoolean(element, "unorderedList"),
            CanIndent = GetBoolean(element, "canIndent", true),
            CanOutdent = GetBoolean(element, "canOutdent"),
            HasSelection = GetBoolean(element, "hasSelection"),
            IsSpellCheckEnabled = GetBoolean(element, "isSpellCheckEnabled", true),
            Alignment = GetString(element, "alignment"),
            FontFamily = GetString(element, "fontFamily"),
            FontSize = GetNullableInt32(element, "fontSize"),
            ParagraphStyle = GetString(element, "paragraphStyle"),
            TextColor = GetString(element, "textColor"),
            HighlightColor = GetString(element, "highlightColor"),
            LineHeight = GetString(element, "lineHeight"),
            LinkUrl = GetString(element, "linkUrl"),
            SelectedText = GetString(element, "selectedText")
        };
    }

    private static bool GetBoolean(JsonElement element, string propertyName, bool defaultValue = false)
    {
        if (element.TryGetProperty(propertyName, out var valueElement) && valueElement.ValueKind is JsonValueKind.True or JsonValueKind.False)
        {
            return valueElement.GetBoolean();
        }

        return defaultValue;
    }

    private static int? GetNullableInt32(JsonElement element, string propertyName)
    {
        if (element.TryGetProperty(propertyName, out var valueElement) && valueElement.ValueKind == JsonValueKind.Number && valueElement.TryGetInt32(out var value))
        {
            return value;
        }

        return null;
    }

    private static string? GetString(JsonElement element, string propertyName)
    {
        if (element.TryGetProperty(propertyName, out var valueElement) && valueElement.ValueKind == JsonValueKind.String)
        {
            return valueElement.GetString();
        }

        return null;
    }

    private static EditorCapabilities BuildCapabilities(IReadOnlyList<string> fonts)
    {
        return new EditorCapabilities
        {
            Fonts = fonts,
            FontSizes = DefaultFontSizes,
            TextColors = DefaultTextColors,
            HighlightColors = DefaultHighlightColors,
            ParagraphStyles = DefaultParagraphStyles,
            LineHeights = DefaultLineHeights,
            Alignments = DefaultAlignments
        };
    }

    private EditorState GetCurrentStateOrDefault()
    {
        return CurrentState ?? CreateDefaultState();
    }

    private EditorState CreateDefaultState()
    {
        return new EditorState
        {
            IsDarkMode = IsEditorDarkMode,
            IsBuiltInToolbarVisible = IsEditorWebViewEditor,
            IsSpellCheckEnabled = true,
            FontFamily = _preferencesService.ComposerFont,
            FontSize = _preferencesService.ComposerFontSize,
            ParagraphStyle = "div",
            TextColor = string.Empty,
            HighlightColor = string.Empty,
            LineHeight = "normal"
        };
    }

    private void UpdateState(EditorState newState)
    {
        if (newState == CurrentState)
        {
            return;
        }

        CurrentState = newState;
        StateChanged?.Invoke(this, CurrentState);
    }

    private void UpdateCapabilities(EditorCapabilities newCapabilities)
    {
        if (newCapabilities == Capabilities)
        {
            return;
        }

        Capabilities = newCapabilities;
        CapabilitiesChanged?.Invoke(this, Capabilities);
    }

    private void DetachChromium(WebView2 chromium, bool closeWebView)
    {
        chromium.CoreWebView2Initialized -= ChromiumInitialized;

        if (chromium.CoreWebView2 != null)
        {
            chromium.CoreWebView2.DOMContentLoaded -= DomLoaded;
            chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived;
        }

        if (closeWebView)
        {
            chromium.Close();
        }
    }

    private void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing && _chromium != null)
            {
                DetachChromium(_chromium, closeWebView: true);
                _chromium = null;
            }

            _disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    private sealed class EditorWebMessage
    {
        [JsonPropertyName("type")]
        public string? Type { get; set; }

        [JsonPropertyName("state")]
        public EditorStateSnapshot? State { get; set; }
    }

    private sealed class EditorStateSnapshot
    {
        [JsonPropertyName("bold")]
        public bool IsBold { get; set; }

        [JsonPropertyName("italic")]
        public bool IsItalic { get; set; }

        [JsonPropertyName("underline")]
        public bool IsUnderline { get; set; }

        [JsonPropertyName("strikethrough")]
        public bool IsStrikethrough { get; set; }

        [JsonPropertyName("orderedList")]
        public bool IsOrderedList { get; set; }

        [JsonPropertyName("unorderedList")]
        public bool IsUnorderedList { get; set; }

        [JsonPropertyName("canIndent")]
        public bool CanIndent { get; set; }

        [JsonPropertyName("canOutdent")]
        public bool CanOutdent { get; set; }

        [JsonPropertyName("hasSelection")]
        public bool HasSelection { get; set; }

        [JsonPropertyName("isSpellCheckEnabled")]
        public bool IsSpellCheckEnabled { get; set; } = true;

        [JsonPropertyName("alignment")]
        public string? Alignment { get; set; }

        [JsonPropertyName("fontFamily")]
        public string? FontFamily { get; set; }

        [JsonPropertyName("fontSize")]
        public int? FontSize { get; set; }

        [JsonPropertyName("paragraphStyle")]
        public string? ParagraphStyle { get; set; }

        [JsonPropertyName("textColor")]
        public string? TextColor { get; set; }

        [JsonPropertyName("highlightColor")]
        public string? HighlightColor { get; set; }

        [JsonPropertyName("lineHeight")]
        public string? LineHeight { get; set; }

        [JsonPropertyName("linkUrl")]
        public string? LinkUrl { get; set; }

        [JsonPropertyName("selectedText")]
        public string? SelectedText { get; set; }
    }
}









