using BookReader.Models;
using BookReader.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace BookReader.ViewModels;

[QueryProperty(nameof(Book), "Book")]
public partial class ReaderViewModel : BaseViewModel
{
    private readonly IDatabaseService _databaseService;
    private readonly ISettingsService _settingsService;

    private double _lastPersistedProgress = -1;
    private string _lastPersistedCfi = string.Empty;
    private string _lastPersistedChapter = string.Empty;
    private int _lastPersistedCurrentPage = -1;
    private int _lastPersistedTotalPages = -1;
    private DateTime _lastPersistedAt = DateTime.MinValue;

    [ObservableProperty]
    private Book? _book;

    [ObservableProperty]
    private bool _isMenuVisible;

    [ObservableProperty]
    private bool _isChapterListVisible;

    [ObservableProperty]
    private int _fontSize;

    [ObservableProperty]
    private string _fontFamily = "serif";

    [ObservableProperty]
    private string _readerTheme = Constants.Reader.DefaultTheme;

    [ObservableProperty]
    private double _brightness = Constants.Reader.DefaultBrightness;

    [ObservableProperty]
    private List<string> _chapters = new();

    [ObservableProperty]
    private string? _selectedChapter;

    [ObservableProperty]
    private int _chapterCurrentPage = 1;

    [ObservableProperty]
    private int _chapterTotalPages = 1;

    [ObservableProperty]
    private int _currentPage = 1;

    [ObservableProperty]
    private int _totalPages = 100;

    public string ChapterProgressText => ChapterTotalPages > 1
        ? $"Глава: {ChapterCurrentPage} из {ChapterTotalPages}"
        : "Позиция внутри главы появится после перелистывания";

    public string ProgressText => TotalPages == 100
        ? $"{CurrentPage}%"
        : $"Стр. {CurrentPage} из {TotalPages}";

    partial void OnChapterCurrentPageChanged(int value) => OnPropertyChanged(nameof(ChapterProgressText));
    partial void OnChapterTotalPagesChanged(int value) => OnPropertyChanged(nameof(ChapterProgressText));
    partial void OnCurrentPageChanged(int value) => OnPropertyChanged(nameof(ProgressText));
    partial void OnTotalPagesChanged(int value) => OnPropertyChanged(nameof(ProgressText));

    public List<string> AvailableFonts { get; } = new()
    {
        "serif",
        "sans-serif",
        "monospace",
        "Georgia",
        "Palatino",
        "Times New Roman",
        "Arial",
        "Verdana",
        "Courier New"
    };

    public List<int> AvailableFontSizes { get; } = new()
    {
        12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 36, 40
    };

    public event Action<string>? OnJavaScriptRequested;

    public ReaderViewModel(
        IDatabaseService databaseService,
        ISettingsService settingsService)
    {
        _databaseService = databaseService;
        _settingsService = settingsService;
        _fontSize = Constants.Reader.DefaultFontSize;
    }

    public async Task InitializeAsync()
    {
        if (Book == null)
        {
            System.Diagnostics.Debug.WriteLine("[ReaderViewModel] Book is null, cannot initialize");
            return;
        }

        if (!File.Exists(Book.FilePath))
        {
            System.Diagnostics.Debug.WriteLine($"[ReaderViewModel] Book file not found: {Book.FilePath}");
            return;
        }

        FontSize = await _settingsService.GetIntAsync(SettingsKeys.DefaultFontSize, Constants.Reader.DefaultFontSize);
        FontFamily = await _settingsService.GetAsync(SettingsKeys.DefaultFontFamily, "serif");
        ReaderTheme = await _settingsService.GetAsync(SettingsKeys.Theme, Constants.Reader.DefaultTheme);
        Brightness = await _settingsService.GetDoubleAsync(SettingsKeys.Brightness, Constants.Reader.DefaultBrightness);

        CurrentPage = Book.CurrentPage > 0 ? Book.CurrentPage : 1;
        TotalPages = Book.TotalPages > 0 ? Book.TotalPages : 100;

        RememberPersistedProgress(Book.ReadingProgress, Book.LastCfi, Book.LastChapter, Book.CurrentPage, Book.TotalPages);
    }

    [RelayCommand]
    public void ToggleMenu()
    {
        IsMenuVisible = !IsMenuVisible;
        if (!IsMenuVisible)
        {
            IsChapterListVisible = false;
        }
    }

    [RelayCommand]
    public void HideMenu()
    {
        IsMenuVisible = false;
        IsChapterListVisible = false;
    }

    [RelayCommand]
    public void ToggleChapterList()
    {
        IsChapterListVisible = !IsChapterListVisible;
    }

    [RelayCommand]
    public void ChangeFontSize(int size)
    {
        FontSize = size;
        OnJavaScriptRequested?.Invoke($"setFontSize({size})");
        _ = _settingsService.SetIntAsync(SettingsKeys.DefaultFontSize, size);
    }

    [RelayCommand]
    public void ChangeFontFamily(string family)
    {
        FontFamily = family;
        OnJavaScriptRequested?.Invoke($"setFontFamily('{EscapeJs(family)}')");
        _ = _settingsService.SetAsync(SettingsKeys.DefaultFontFamily, family);
    }

    [RelayCommand]
    public void ChangeReaderTheme(string theme)
    {
        ReaderTheme = theme;
        OnJavaScriptRequested?.Invoke($"setReaderTheme('{EscapeJs(theme)}')");
        _ = _settingsService.SetAsync(SettingsKeys.Theme, theme);
    }

    [RelayCommand]
    public void ChangeBrightness(double brightness)
    {
        Brightness = Math.Clamp(brightness, Constants.Reader.MinBrightness, Constants.Reader.MaxBrightness);
        OnJavaScriptRequested?.Invoke($"setBrightness({Brightness.ToString(System.Globalization.CultureInfo.InvariantCulture)})");
        _ = _settingsService.SetDoubleAsync(SettingsKeys.Brightness, Brightness);
    }

    [RelayCommand]
    public void GoToChapter(string chapter)
    {
        if (string.IsNullOrEmpty(chapter))
        {
            return;
        }

        OnJavaScriptRequested?.Invoke($"goToChapter('{EscapeJs(chapter)}')");
        IsChapterListVisible = false;
        IsMenuVisible = false;
    }

    public async Task SaveLocationsAsync(string locations)
    {
        if (Book == null)
        {
            System.Diagnostics.Debug.WriteLine("[ReaderViewModel] Cannot save locations: Book is null");
            return;
        }

        Book.Locations = locations;
        await _databaseService.UpdateBookAsync(Book);
    }

    public async Task SaveProgressAsync(double progress, string? cfi, string? chapter, int currentPage, int totalPages, bool force = false)
    {
        if (Book == null)
        {
            System.Diagnostics.Debug.WriteLine("[ReaderViewModel] Cannot save progress: Book is null");
            return;
        }

        if (string.IsNullOrEmpty(cfi) && progress <= 0)
        {
            return;
        }

        CurrentPage = currentPage > 0 ? currentPage : CurrentPage;
        TotalPages = totalPages > 0 ? totalPages : TotalPages;

        Book.ReadingProgress = progress;
        Book.LastCfi = cfi;
        Book.LastChapter = chapter;
        Book.CurrentPage = currentPage;
        Book.TotalPages = totalPages;
        Book.LastRead = DateTime.UtcNow;

        var hasMeaningfulChange = HasMeaningfulProgressChange(progress, cfi, chapter, currentPage, totalPages);
        if (!force)
        {
            var throttle = TimeSpan.FromSeconds(Constants.Reader.ProgressSaveThrottleSeconds);
            var shouldPersistNow = hasMeaningfulChange &&
                                   (DateTime.UtcNow - _lastPersistedAt >= throttle || Math.Abs(progress - _lastPersistedProgress) >= 0.02 || currentPage != _lastPersistedCurrentPage);

            if (!shouldPersistNow)
            {
                return;
            }
        }
        else if (!hasMeaningfulChange)
        {
            return;
        }

        await _databaseService.UpdateBookAsync(Book);

        await _databaseService.SaveProgressAsync(new ReadingProgress
        {
            BookId = Book.Id,
            Cfi = cfi,
            Progress = progress,
            CurrentPage = currentPage,
            ChapterTitle = chapter
        });

        RememberPersistedProgress(progress, cfi, chapter, currentPage, totalPages);
    }

    public string? GetLastCfi() => Book?.LastCfi;

    public string? GetLocations() => Book?.Locations;

    private bool HasMeaningfulProgressChange(double progress, string? cfi, string? chapter, int currentPage, int totalPages)
    {
        return Math.Abs(progress - _lastPersistedProgress) >= 0.005 ||
               string.Equals(cfi ?? string.Empty, _lastPersistedCfi, StringComparison.Ordinal) == false ||
               string.Equals(chapter ?? string.Empty, _lastPersistedChapter, StringComparison.Ordinal) == false ||
               currentPage != _lastPersistedCurrentPage ||
               totalPages != _lastPersistedTotalPages;
    }

    private void RememberPersistedProgress(double progress, string? cfi, string? chapter, int currentPage, int totalPages)
    {
        _lastPersistedProgress = progress;
        _lastPersistedCfi = cfi ?? string.Empty;
        _lastPersistedChapter = chapter ?? string.Empty;
        _lastPersistedCurrentPage = currentPage;
        _lastPersistedTotalPages = totalPages;
        _lastPersistedAt = DateTime.UtcNow;
    }

    private static string EscapeJs(string value)
    {
        return value.Replace("\\", "\\\\").Replace("'", "\\'").Replace("\n", "\\n").Replace("\r", "\\r");
    }
}
