using BookReader.ViewModels;
using Newtonsoft.Json.Linq;

namespace BookReader.Views;

public partial class ReaderPage : ContentPage
{
    private readonly ReaderViewModel _viewModel;
    private bool _isBookLoaded;
    private readonly List<JObject> _chapterData = new();
    private IDispatcherTimer? _pollTimer;
    private IDispatcherTimer? _progressTimer;
    private bool _isActive;

    public ReaderPage(ReaderViewModel viewModel)
    {
        InitializeComponent();
        _viewModel = viewModel;
        BindingContext = viewModel;
        _viewModel.OnJavaScriptRequested += OnJavaScriptRequested;
    }

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

        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;
        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)
            {
                return;
            }

            if (_isBookLoaded)
            {
                return;
            }

            if (!File.Exists(book.FilePath))
            {
                await DisplayAlert("Error", "Book file not found", "OK");
                return;
            }


            // Читаем файл и конвертируем в Base64
            var fileBytes = await File.ReadAllBytesAsync(book.FilePath);
            var base64 = Convert.ToBase64String(fileBytes);
            var format = book.Format.ToLowerInvariant();
            var lastCfi = _viewModel.GetLastCfi() ?? "";
            var locations=_viewModel.GetLocations() ?? "";

            // Отправляем данные чанками чтобы не превысить лимит JS строки
            const int chunkSize = 400_000;

            if (base64.Length > chunkSize)
            {
                var chunks = SplitString(base64, chunkSize);

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

                for (int i = 0; i < chunks.Count; i++)
                {
                    await EvalJsAsync($"window._bkChunks.push('{chunks[i]}');");
                }

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

            _isBookLoaded = true;
            System.Diagnostics.Debug.WriteLine("[Reader] Book load command sent");



            // Применяем настройки шрифта сразу
            await EvalJsAsync($"window.setFontSize({_viewModel.FontSize})");
            await EvalJsAsync($"window.setFontFamily('{EscapeJs(_viewModel.FontFamily)}')");

            System.Diagnostics.Debug.WriteLine("[Reader] Book fully loaded");
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] Load error: {ex.Message}\n{ex.StackTrace}");
            await DisplayAlert("Error", $"Failed to load book: {ex.Message}", "OK");
        }
    }

    // ========== СОХРАНЕНИЕ ПРОГРЕССА ==========

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

        try
        {
            var result = await EvalJsWithResultAsync("window.getProgress()");
            if (string.IsNullOrEmpty(result) || result == "null" || result == "{}" || result == "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, null, currentPage, totalPages);
            System.Diagnostics.Debug.WriteLine($"[Reader] Saved progress: {progress:P0}");
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] Save progress error: {ex.Message}");
        }
    }

    // ========== ОБРАБОТКА СООБЩЕНИЙ ОТ JS ==========

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

            // ... (оставляем логику логирования и парсинга JSON) ...
            var json = JObject.Parse(message);
            var action = json["action"]?.ToString();
            var data = json["data"] as JObject;

            switch (action)
            {
                case "readerReady":
                   
                    // Вызываем загрузку книги ТОЛЬКО после того, как JS подтвердил готовность
                    _ = 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;
                        // Обновляем ViewModel на главном потоке
                        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();
                        _chapterData.Clear();
                        _chapterData.AddRange(chapters);
                        MainThread.BeginInvokeOnMainThread(() =>
                        {
                            _viewModel.Chapters = chapters
                                .Select(c => c["label"]?.ToString() ?? "")
                                .Where(l => !string.IsNullOrWhiteSpace(l))
                                .ToList();
                        });
                    }
                    break;

                case "bookReady":
                    if (data != null)
                    {
                        MainThread.BeginInvokeOnMainThread(async () =>
                        {
                            await EvalJsAsync($"window.setFontSize({_viewModel.FontSize})");
                            await EvalJsAsync($"window.setFontFamily('{EscapeJs(_viewModel.FontFamily)}')");
                        });
                    }
                    break;

                case "saveLocations":
                    // Извлекаем строку локаций из данных
                    string locations = data["locations"]?.ToString();
                    // Сохраняем в базу данных
                    await _viewModel.SaveLocationsAsync(locations);
                    break;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Reader] RawMsg error: {ex.Message}");
        }
    }

    // ========== ОБРАБОТКА ЗАПРОСОВ JS ОТ VIEWMODEL ==========

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

    // ========== UI EVENTS ==========

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

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

    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 chapterObj = _chapterData.FirstOrDefault(c => c["label"]?.ToString() == chapterLabel);
            var href = chapterObj?["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 Shell.Current.GoToAsync("..");
    }

    // ========== ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ ==========

    /// <summary>
    /// Выполняет JavaScript без ожидания результата
    /// </summary>
    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}");
        }
    }

    /// <summary>
    /// Выполняет JavaScript и возвращает результат
    /// </summary>
    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;
    }

    /// <summary>
    /// Разбивает строку на чанки заданного размера
    /// </summary>
    private static List<string> SplitString(string str, int chunkSize)
    {
        var chunks = new List<string>();
        for (int i = 0; i < str.Length; i += chunkSize)
        {
            chunks.Add(str.Substring(i, Math.Min(chunkSize, str.Length - i)));
        }
        return chunks;
    }

    /// <summary>
    /// Экранирует строку для вставки в JS код (внутри одинарных кавычек)
    /// </summary>
    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");
    }

    /// <summary>
    /// Убирает экранирование из результата EvaluateJavaScriptAsync.
    /// Android WebView оборачивает результат в кавычки и экранирует.
    /// </summary>
    private static string UnescapeJsResult(string result)
    {
        if (string.IsNullOrEmpty(result))
            return result;

        // Убираем обрамляющие кавычки если есть
        if (result.StartsWith("\"") && result.EndsWith("\""))
        {
            result = result.Substring(1, result.Length - 2);
        }

        // Убираем экранирование
        result = result
            .Replace("\\\"", "\"")
            .Replace("\\\\", "\\")
            .Replace("\\/", "/")
            .Replace("\\n", "\n")
            .Replace("\\r", "\r")
            .Replace("\\t", "\t");

        return result;
    }
}