using BookReader.Services;
using BookReader.ViewModels;
using Newtonsoft.Json.Linq;
using System.Globalization;

namespace BookReader.Views;

public partial class ReaderPage : ContentPage
{
    private readonly ReaderViewModel _viewModel;
    private readonly INavigationService _navigationService;
    private readonly List<JObject> _chapterData = new();
    private bool _isActive;
    private bool _isBookLoaded;
    private bool _isSubscribedToJavaScriptRequests;

    public ReaderPage(ReaderViewModel viewModel, INavigationService navigationService)
    {
        InitializeComponent();
        _viewModel = viewModel;
        _navigationService = navigationService;
        BindingContext = viewModel;
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();
        _isActive = true;
        EnsureSubscribed();

        try
        {
            await _viewModel.InitializeAsync();
            System.Diagnostics.Debug.WriteLine("[Reader] ViewModel initialized");
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] Init error: {ex.Message}");
        }
    }

    protected override async void OnDisappearing()
    {
        _isActive = false;
        EnsureUnsubscribed();
        base.OnDisappearing();
        await SaveCurrentProgress();
    }

    protected override async void OnNavigatedFrom(NavigatedFromEventArgs args)
    {
        _isActive = false;
        base.OnNavigatedFrom(args);
        await SaveCurrentProgress();
    }

    private async Task LoadBookIntoWebView()
    {
        try
        {
            var book = _viewModel.Book;
            if (book == null || _isBookLoaded)
            {
                return;
            }

            if (!File.Exists(book.FilePath))
            {
                await _navigationService.DisplayAlertAsync("Ошибка", "Файл книги не найден.", "OK");
                return;
            }

            var format = book.Format.ToLowerInvariant();
            var lastCfi = _viewModel.GetLastCfi() ?? string.Empty;
            var locations = _viewModel.GetLocations() ?? string.Empty;

            await EvalJsAsync("window._bkChunks = [];");

            using (var fileStream = File.OpenRead(book.FilePath))
            {
                var buffer = new byte[Constants.Reader.Base64RawChunkSize];
                int bytesRead;

                while ((bytesRead = await fileStream.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0)
                {
                    var base64Chunk = Convert.ToBase64String(buffer, 0, bytesRead);
                    await EvalJsAsync($"window._bkChunks.push('{base64Chunk}');");
                }
            }

            await EvalJsAsync(
                $"window.loadBookFromBase64(window._bkChunks.join(''), '{format}', '{EscapeJs(lastCfi)}', '{EscapeJs(locations)}');");
            await EvalJsAsync("delete window._bkChunks;");

            _isBookLoaded = true;

            await EvalJsAsync($"window.setFontSize({_viewModel.FontSize.ToString(CultureInfo.InvariantCulture)})");
            await EvalJsAsync($"window.setFontFamily('{EscapeJs(_viewModel.FontFamily)}')");
            await EvalJsAsync($"window.setReaderTheme('{EscapeJs(_viewModel.ReaderTheme)}')");
            await EvalJsAsync($"window.setBrightness({_viewModel.Brightness.ToString(CultureInfo.InvariantCulture)})");
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] Load error: {ex.Message}\n{ex.StackTrace}");
            await _navigationService.DisplayAlertAsync("Ошибка", $"Не удалось загрузить книгу: {ex.Message}", "OK");
        }
    }

    private async Task SaveCurrentProgress()
    {
        if (!_isBookLoaded)
        {
            return;
        }

        try
        {
            var result = await EvalJsWithResultAsync("window.getProgress()");
            if (string.IsNullOrEmpty(result) || result is "null" or "{}" or "undefined")
            {
                return;
            }

            result = UnescapeJsResult(result);
            var data = JObject.Parse(result);
            var progress = data["progress"]?.Value<double>() ?? 0;
            var cfi = data["cfi"]?.ToString();
            var currentPage = data["currentPage"]?.Value<int>() ?? 0;
            var totalPages = data["totalPages"]?.Value<int>() ?? 0;

            await _viewModel.SaveProgressAsync(progress, cfi, _viewModel.Book?.LastChapter, currentPage, totalPages, force: true);
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] Save progress error: {ex.Message}");
        }
    }

    private async void OnRawMessageReceived(object? sender, HybridWebViewRawMessageReceivedEventArgs e)
    {
        try
        {
            var message = e.Message;
            if (string.IsNullOrEmpty(message))
            {
                return;
            }

            var json = JObject.Parse(message);
            var action = json["action"]?.ToString();
            var data = json["data"] as JObject;

            switch (action)
            {
                case "readerReady":
                    _ = MainThread.InvokeOnMainThreadAsync(LoadBookIntoWebView);
                    break;

                case "toggleMenu":
                    MainThread.BeginInvokeOnMainThread(() => _viewModel.ToggleMenuCommand.Execute(null));
                    break;

                case "progressUpdate":
                    if (data != null)
                    {
                        var progress = data["progress"]?.Value<double>() ?? 0;
                        var cfi = data["cfi"]?.ToString();
                        var chapter = data["chapter"]?.ToString();
                        var currentPage = data["currentPage"]?.Value<int>() ?? 0;
                        var totalPages = data["totalPages"]?.Value<int>() ?? 0;
                        var chapterPage = data["chapterCurrentPage"]?.Value<int>() ?? 1;
                        var chapterTotal = data["chapterTotalPages"]?.Value<int>() ?? 1;

                        MainThread.BeginInvokeOnMainThread(() =>
                        {
                            _viewModel.ChapterCurrentPage = chapterPage;
                            _viewModel.ChapterTotalPages = chapterTotal;
                            _viewModel.TotalPages = totalPages;
                            _viewModel.CurrentPage = currentPage;
                        });

                        await _viewModel.SaveProgressAsync(progress, cfi, chapter, currentPage, totalPages);
                    }
                    break;

                case "chaptersLoaded":
                    if (data != null)
                    {
                        var chapters = data["chapters"]?.ToObject<List<JObject>>() ?? new List<JObject>();
                        _chapterData.Clear();
                        _chapterData.AddRange(chapters);
                        MainThread.BeginInvokeOnMainThread(() =>
                        {
                            _viewModel.Chapters = chapters
                                .Select(chapter => chapter["label"]?.ToString() ?? string.Empty)
                                .Where(label => !string.IsNullOrWhiteSpace(label))
                                .ToList();
                        });
                    }
                    break;

                case "bookReady":
                    MainThread.BeginInvokeOnMainThread(async () =>
                    {
                        await EvalJsAsync($"window.setFontSize({_viewModel.FontSize.ToString(CultureInfo.InvariantCulture)})");
                        await EvalJsAsync($"window.setFontFamily('{EscapeJs(_viewModel.FontFamily)}')");
                        await EvalJsAsync($"window.setReaderTheme('{EscapeJs(_viewModel.ReaderTheme)}')");
                        await EvalJsAsync($"window.setBrightness({_viewModel.Brightness.ToString(CultureInfo.InvariantCulture)})");
                    });
                    break;

                case "saveLocations":
                    if (data != null)
                    {
                        var locations = data["locations"]?.ToString() ?? string.Empty;
                        await _viewModel.SaveLocationsAsync(locations);
                    }
                    break;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] RawMsg error: {ex.Message}");
        }
    }

    private async void OnJavaScriptRequested(string script)
    {
        if (!_isActive)
        {
            return;
        }

        await EvalJsAsync(script);
    }

    private void OnDecreaseFontSize(object? sender, EventArgs e)
    {
        var index = _viewModel.AvailableFontSizes.IndexOf(_viewModel.FontSize);
        if (index > 0)
        {
            _viewModel.ChangeFontSizeCommand.Execute(_viewModel.AvailableFontSizes[index - 1]);
        }
    }

    private void OnIncreaseFontSize(object? sender, EventArgs e)
    {
        var index = _viewModel.AvailableFontSizes.IndexOf(_viewModel.FontSize);
        if (index < _viewModel.AvailableFontSizes.Count - 1)
        {
            _viewModel.ChangeFontSizeCommand.Execute(_viewModel.AvailableFontSizes[index + 1]);
        }
    }

    private void OnSepiaThemeClicked(object? sender, EventArgs e)
    {
        _viewModel.ChangeReaderThemeCommand.Execute("sepia");
    }

    private void OnLightThemeClicked(object? sender, EventArgs e)
    {
        _viewModel.ChangeReaderThemeCommand.Execute("light");
    }

    private void OnDarkThemeClicked(object? sender, EventArgs e)
    {
        _viewModel.ChangeReaderThemeCommand.Execute("dark");
    }

    private void OnBrightnessChanged(object? sender, ValueChangedEventArgs e)
    {
        _viewModel.ChangeBrightnessCommand.Execute(e.NewValue);
    }

    private void OnFontFamilyChanged(object? sender, EventArgs e)
    {
        if (FontFamilyPicker.SelectedItem is string family)
        {
            _viewModel.ChangeFontFamilyCommand.Execute(family);
        }
    }

    private void OnChapterSelected(object? sender, SelectionChangedEventArgs e)
    {
        if (e.CurrentSelection.FirstOrDefault() is string chapterLabel)
        {
            var chapterObject = _chapterData.FirstOrDefault(chapter => chapter["label"]?.ToString() == chapterLabel);
            var href = chapterObject?["href"]?.ToString() ?? chapterLabel;
            _viewModel.GoToChapterCommand.Execute(href);
        }
    }

    private void OnMenuPanelTapped(object? sender, TappedEventArgs e)
    {
    }

    private async void OnBackToLibrary(object? sender, EventArgs e)
    {
        await SaveCurrentProgress();
        await _navigationService.GoBackAsync();
    }

    private void EnsureSubscribed()
    {
        if (_isSubscribedToJavaScriptRequests)
        {
            return;
        }

        _viewModel.OnJavaScriptRequested += OnJavaScriptRequested;
        _isSubscribedToJavaScriptRequests = true;
    }

    private void EnsureUnsubscribed()
    {
        if (!_isSubscribedToJavaScriptRequests)
        {
            return;
        }

        _viewModel.OnJavaScriptRequested -= OnJavaScriptRequested;
        _isSubscribedToJavaScriptRequests = false;
    }

    private async Task EvalJsAsync(string script)
    {
        try
        {
            await MainThread.InvokeOnMainThreadAsync(async () =>
            {
                try
                {
                    await ReaderWebView.EvaluateJavaScriptAsync(script);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine($"[Reader] JS eval error: {ex.Message}");
                }
            });
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] JS dispatch error: {ex.Message}");
        }
    }

    private async Task<string?> EvalJsWithResultAsync(string script)
    {
        string? result = null;
        try
        {
            await MainThread.InvokeOnMainThreadAsync(async () =>
            {
                try
                {
                    result = await ReaderWebView.EvaluateJavaScriptAsync(script);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine($"[Reader] JS result error: {ex.Message}");
                }
            });
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] JS result dispatch error: {ex.Message}");
        }

        return result;
    }

    private static string EscapeJs(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return string.Empty;
        }

        return value
            .Replace("\\", "\\\\")
            .Replace("'", "\\'")
            .Replace("\"", "\\\"")
            .Replace("\n", "\\n")
            .Replace("\r", "\\r")
            .Replace("\t", "\\t");
    }

    private static string UnescapeJsResult(string result)
    {
        if (string.IsNullOrEmpty(result))
        {
            return result;
        }

        if (result.StartsWith("\"") && result.EndsWith("\""))
        {
            result = result.Substring(1, result.Length - 2);
        }

        return result
            .Replace("\\\"", "\"")
            .Replace("\\\\", "\\")
            .Replace("\\/", "/")
            .Replace("\\n", "\n")
            .Replace("\\r", "\r")
            .Replace("\\t", "\t");
    }
}

