// EN: Auth state service — holds current session state in memory.
//     Token is NOT stored here (it lives in the httpOnly BFF cookie).
//     Tracks token expiry and fires OnTokenExpiring 2 minutes before expiry for proactive refresh.
// VI: Service trạng thái auth — lưu trạng thái session hiện tại trong memory.
//     Token KHÔNG được lưu ở đây (nó ở trong httpOnly BFF cookie).
//     Theo dõi thời hạn token và kích hoạt OnTokenExpiring trước 2 phút để refresh chủ động.

namespace WebClientTpos.Client.Services;

/// <summary>
/// EN: Singleton auth state service. Tracks authentication state, role, and token expiry.
///     Does NOT store the raw access token — that is managed server-side via httpOnly cookie.
/// VI: Singleton service trạng thái auth. Theo dõi trạng thái xác thực, role, và thời hạn token.
///     KHÔNG lưu access token thô — được quản lý phía server qua httpOnly cookie.
/// </summary>
public class AuthStateService : IDisposable
{
    // ═══════════════════════════════════════════════════════════════════
    // EN: Public state
    // VI: Trạng thái công khai
    // ═══════════════════════════════════════════════════════════════════

    public bool IsAuthenticated { get; private set; }
    public string? UserEmail { get; private set; }

    /// <summary>
    /// EN: User role — "owner", "staff", "customer", "branch".
    /// VI: Vai trò người dùng — "owner", "staff", "customer", "branch".
    /// </summary>
    public string? UserRole { get; private set; }

    /// <summary>
    /// EN: UTC time when the session cookie expires. Null if unknown.
    ///     Used to schedule proactive token refresh.
    /// VI: Thời điểm UTC khi session cookie hết hạn. Null nếu không biết.
    ///     Dùng để lên lịch refresh token chủ động.
    /// </summary>
    public DateTime? TokenExpiry { get; private set; }

    // ═══════════════════════════════════════════════════════════════════
    // EN: Events
    // VI: Sự kiện
    // ═══════════════════════════════════════════════════════════════════

    /// <summary>
    /// EN: Fired whenever authentication state changes (login, logout, refresh).
    /// VI: Kích hoạt khi trạng thái xác thực thay đổi (đăng nhập, đăng xuất, refresh).
    /// </summary>
    public event Action? OnChange;

    /// <summary>
    /// EN: Fired ~2 minutes before token expiry to trigger a proactive refresh.
    ///     Subscribers (e.g. AuthService) should call TryRestoreSessionAsync to renew the cookie.
    /// VI: Kích hoạt ~2 phút trước khi token hết hạn để trigger refresh chủ động.
    ///     Subscriber (ví dụ AuthService) nên gọi TryRestoreSessionAsync để gia hạn cookie.
    /// </summary>
    public event Action? OnTokenExpiring;

    // ═══════════════════════════════════════════════════════════════════
    // EN: Timer for proactive refresh
    // VI: Timer để refresh chủ động
    // ═══════════════════════════════════════════════════════════════════

    private Timer? _refreshTimer;

    // EN: Fire refresh event this many seconds before actual expiry.
    // VI: Kích hoạt sự kiện refresh trước thời điểm hết hạn thực tế bao nhiêu giây.
    private const int RefreshLeadSeconds = 120;

    // ═══════════════════════════════════════════════════════════════════
    // EN: State mutations
    // VI: Thay đổi trạng thái
    // ═══════════════════════════════════════════════════════════════════

    /// <summary>
    /// EN: Record a successful login. Token is managed by the BFF httpOnly cookie — not stored here.
    /// VI: Ghi nhận đăng nhập thành công. Token được quản lý bởi BFF httpOnly cookie — không lưu ở đây.
    /// </summary>
    public void Login(string email, string role, DateTime? expiresAt = null)
    {
        if (IsAuthenticated && UserEmail == email && UserRole == role && TokenExpiry == expiresAt)
            return;

        IsAuthenticated = true;
        UserEmail = email;
        UserRole = role;
        TokenExpiry = expiresAt;

        ScheduleRefresh(expiresAt);
        OnChange?.Invoke();
    }

    /// <summary>
    /// EN: Clear session state on logout or expiry.
    /// VI: Xóa trạng thái session khi đăng xuất hoặc hết hạn.
    /// </summary>
    public void Logout()
    {
        IsAuthenticated = false;
        UserEmail = null;
        UserRole = null;
        TokenExpiry = null;

        CancelRefreshTimer();
        OnChange?.Invoke();
    }

    // ═══════════════════════════════════════════════════════════════════
    // EN: Token expiry scheduling
    // VI: Lên lịch hết hạn token
    // ═══════════════════════════════════════════════════════════════════

    private void ScheduleRefresh(DateTime? expiresAt)
    {
        CancelRefreshTimer();

        if (!expiresAt.HasValue)
            return;

        // EN: Calculate when to fire the refresh event (lead time before actual expiry).
        // VI: Tính thời điểm kích hoạt sự kiện refresh (trước thời điểm hết hạn thực tế).
        var now = DateTime.UtcNow;
        var fireAt = expiresAt.Value.AddSeconds(-RefreshLeadSeconds);
        var delay = fireAt - now;

        // EN: If already within lead window (or expired), fire immediately.
        // VI: Nếu đã trong cửa sổ lead (hoặc hết hạn), kích hoạt ngay lập tức.
        if (delay <= TimeSpan.Zero)
        {
            OnTokenExpiring?.Invoke();
            return;
        }

        _refreshTimer = new Timer(_ =>
        {
            OnTokenExpiring?.Invoke();
            CancelRefreshTimer();
        }, null, delay, Timeout.InfiniteTimeSpan);
    }

    private void CancelRefreshTimer()
    {
        _refreshTimer?.Dispose();
        _refreshTimer = null;
    }

    // ═══════════════════════════════════════════════════════════════════
    // EN: Navigation helpers
    // VI: Helper điều hướng
    // ═══════════════════════════════════════════════════════════════════

    /// <summary>
    /// EN: Returns the portal URL for the current user role.
    /// VI: Trả về URL portal cho vai trò người dùng hiện tại.
    /// </summary>
    public string GetPortalUrl() => UserRole switch
    {
        "owner" or "admin" => "/admin",
        "staff"            => "/staff/dashboard",
        "branch"           => "/admin",
        "customer"         => "/app",
        _                  => "/auth/login"
    };

    // ═══════════════════════════════════════════════════════════════════
    // EN: IDisposable
    // VI: IDisposable
    // ═══════════════════════════════════════════════════════════════════

    public void Dispose()
    {
        CancelRefreshTimer();
        GC.SuppressFinalize(this);
    }
}
