using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace YMhut.Box.Core.Settings;

public sealed class AppSettingsService(AppSettingsStore store) : ISettingsService
{
    private readonly SemaphoreSlim _gate = new(1, 1);
    private AppSettings _current = new();

    public event PropertyChangedEventHandler? PropertyChanged;

    public AppSettings Current => _current;

    public string SettingsPath => store.SettingsPath;

    public async Task<AppSettings> LoadAsync(CancellationToken cancellationToken = default)
    {
        await _gate.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            _current = await store.LoadAsync(cancellationToken).ConfigureAwait(false);
            OnPropertyChanged(nameof(Current));
            return _current;
        }
        finally
        {
            _gate.Release();
        }
    }

    public async Task SaveAsync(CancellationToken cancellationToken = default)
    {
        await _gate.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            await store.SaveAsync(_current, cancellationToken).ConfigureAwait(false);
            OnPropertyChanged(nameof(Current));
        }
        finally
        {
            _gate.Release();
        }
    }

    public async Task UpdateAsync(Action<AppSettings> update, CancellationToken cancellationToken = default)
    {
        await _gate.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            if (_current is null)
            {
                _current = await store.LoadAsync(cancellationToken).ConfigureAwait(false);
            }

            update(_current);
            await store.SaveAsync(_current, cancellationToken).ConfigureAwait(false);
            OnPropertyChanged(nameof(Current));
        }
        finally
        {
            _gate.Release();
        }
    }

    public async Task RecordRecentToolAsync(string toolId, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(toolId))
        {
            return;
        }

        await UpdateAsync(settings =>
        {
            settings.RecentToolIds.RemoveAll(id => string.Equals(id, toolId, StringComparison.OrdinalIgnoreCase));
            settings.RecentToolIds.Insert(0, toolId);
            if (settings.RecentToolIds.Count > 12)
            {
                settings.RecentToolIds.RemoveRange(12, settings.RecentToolIds.Count - 12);
            }
        }, cancellationToken).ConfigureAwait(false);
    }

    public Task SaveWindowBoundsAsync(
        double x,
        double y,
        double width,
        double height,
        bool maximized,
        CancellationToken cancellationToken = default)
    {
        return UpdateAsync(settings =>
        {
            settings.WindowX = x;
            settings.WindowY = y;
            settings.WindowWidth = Math.Max(640, width);
            settings.WindowHeight = Math.Max(420, height);
            settings.WindowMaximized = maximized;
        }, cancellationToken);
    }

    private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
