using Microsoft.JSInterop;
using NexusReader.Domain.Enums;
using NexusReader.Application.Abstractions.Services;

namespace NexusReader.UI.Shared.Services;

public sealed class ThemeService : IThemeService
{
    private readonly IJSRuntime _jsRuntime;
    private readonly IUserPreferenceStore _userPreferenceStore;
    private readonly SemaphoreSlim _semaphore = new(1, 1);
    private bool _isInitialized;
    private bool _systemPrefersLight;

    public ThemeMode Mode { get; private set; } = ThemeMode.System;

    public bool IsLightMode => Mode == ThemeMode.LightSepia || (Mode == ThemeMode.System && _systemPrefersLight);

    public event Action<ThemeMode>? OnThemeChanged;

    public ThemeService(IJSRuntime jsRuntime, IUserPreferenceStore userPreferenceStore)
    {
        _jsRuntime = jsRuntime;
        _userPreferenceStore = userPreferenceStore;
    }

    public async Task InitializeAsync()
    {
        if (_isInitialized) return;

        await _semaphore.WaitAsync();
        try
        {
            if (_isInitialized) return;

            ThemeMode localMode = ThemeMode.System;
            try
            {
                var cachedThemeVal = await _jsRuntime.InvokeAsync<string?>("localStorage.getItem", "theme-mode");
                if (Enum.TryParse<ThemeMode>(cachedThemeVal, out var parsedMode))
                {
                    localMode = parsedMode;
                }
                else if (cachedThemeVal == "light" || cachedThemeVal == "theme-light")
                {
                    localMode = ThemeMode.LightSepia;
                }
                else if (cachedThemeVal == "dark" || cachedThemeVal == "theme-dark")
                {
                    localMode = ThemeMode.Dark;
                }
                
                _systemPrefersLight = await _jsRuntime.InvokeAsync<bool>("themeInterop.isSystemLight");
            }
            catch
            {
                // Silent catch for pre-rendering or unit tests
            }

            Mode = localMode;
            _isInitialized = true;
            
            await ApplyThemeToDomAsync(Mode);

            // Asynchronously sync with the cloud to check for updates from other devices
            _ = Task.Run(async () =>
            {
                try
                {
                    var cloudResult = await _userPreferenceStore.GetThemePreferenceAsync();
                    if (cloudResult.IsSuccess && cloudResult.Value != Mode)
                    {
                        await SetThemeInternalAsync(cloudResult.Value, saveToCloud: false);
                    }
                }
                catch
                {
                    // Fail silently for background task/network errors
                }
            });
        }
        finally
        {
            _semaphore.Release();
        }
    }

    public async Task SetThemeAsync(ThemeMode mode)
    {
        await SetThemeInternalAsync(mode, saveToCloud: true);
    }

    private async Task SetThemeInternalAsync(ThemeMode mode, bool saveToCloud)
    {
        await _semaphore.WaitAsync();
        try
        {
            if (Mode == mode && _isInitialized) return;

            Mode = mode;
            _isInitialized = true;

            await ApplyThemeToDomAsync(mode);

            OnThemeChanged?.Invoke(mode);

            if (saveToCloud)
            {
                _ = Task.Run(async () =>
                {
                    try
                    {
                        await _userPreferenceStore.SaveThemePreferenceAsync(mode);
                    }
                    catch
                    {
                        // Fail silently for background cloud sync errors
                    }
                });
            }
        }
        finally
        {
            _semaphore.Release();
        }
    }

    public async Task ToggleTheme()
    {
        var nextMode = IsLightMode ? ThemeMode.Dark : ThemeMode.LightSepia;
        await SetThemeAsync(nextMode);
    }

    private async Task ApplyThemeToDomAsync(ThemeMode mode)
    {
        try
        {
            string themeClass = "theme-dark"; // Default
            if (mode == ThemeMode.LightSepia)
            {
                themeClass = "theme-light";
            }
            else if (mode == ThemeMode.System)
            {
                themeClass = _systemPrefersLight ? "theme-light" : "theme-dark";
            }

            await _jsRuntime.InvokeVoidAsync("themeInterop.setCachedTheme", themeClass, ((int)mode).ToString());
        }
        catch
        {
            // Silent catch for pre-rendering
        }
    }
}
