// EN: Authentication service — delegates all token exchange to the BFF server.
//     WASM never handles raw access tokens; they are stored in httpOnly cookies by the BFF.
// VI: Service xác thực — ủy thác trao đổi token cho BFF server.
//     WASM không bao giờ xử lý access token thô; chúng được BFF lưu trong httpOnly cookie.

using System.Net.Http.Json;
using System.Text.Json;
using WebClientTpos.Shared.DTOs;
using WebClientTpos.Shared;

namespace WebClientTpos.Client.Services;

/// <summary>
/// EN: Authentication service — proxies all auth operations through the BFF server.
///     No OAuth2 credentials or raw tokens are present in the WASM client.
/// VI: Service xác thực — proxy tất cả thao tác auth qua BFF server.
///     Không có OAuth2 credentials hay token thô trong WASM client.
/// </summary>
public class AuthService
{
    private readonly HttpClient _http;
    private readonly LocalStorageService _localStorage;
    private readonly AuthStateService _authState;

    // EN: localStorage keys for non-sensitive session metadata (email/name for UI display only).
    //     The access token is NEVER stored in localStorage — it lives in an httpOnly BFF cookie.
    // VI: Các key localStorage cho metadata session không nhạy cảm (email/name chỉ để hiển thị UI).
    //     Access token KHÔNG BAO GIỜ được lưu trong localStorage — nó ở trong httpOnly BFF cookie.
    private const string StoredEmailKey = "aPOS_email";
    private const string StoredRoleKey = "aPOS_role";

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true
    };

    public AuthService(HttpClient http, LocalStorageService localStorage, AuthStateService authState)
    {
        _http = http;
        _localStorage = localStorage;
        _authState = authState;
    }

    /// <summary>
    /// EN: Register a new user account via BFF → IAM service.
    /// VI: Đăng ký tài khoản mới qua BFF → IAM service.
    /// </summary>
    public async Task<(bool Success, string? Error)> RegisterAsync(RegisterDto dto)
    {
        try
        {
            // EN: PascalCase serialization to match IAM RegisterUserCommand record params.
            // VI: Serialization PascalCase để khớp với record params IAM RegisterUserCommand.
            var payload = new { dto.Email, dto.Password, dto.FirstName, dto.LastName };
            var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = null };
            var jsonContent = JsonContent.Create(payload, options: jsonOptions);

            var response = await _http.PostAsync("/api/auth/register", jsonContent);
            if (response.IsSuccessStatusCode)
                return (true, null);

            var content = await response.Content.ReadAsStringAsync();
            try
            {
                using var doc = JsonDocument.Parse(content);
                if (doc.RootElement.TryGetProperty("errors", out var errors))
                {
                    var msgs = new List<string>();
                    foreach (var prop in errors.EnumerateObject())
                        foreach (var err in prop.Value.EnumerateArray())
                            msgs.Add(err.GetString() ?? prop.Name);
                    return (false, string.Join("; ", msgs));
                }
                if (doc.RootElement.TryGetProperty("title", out var title))
                    return (false, title.GetString());
                return (false, content.Length > 200 ? "Đăng ký thất bại" : content);
            }
            catch { return (false, "Đăng ký thất bại"); }
        }
        catch (Exception ex)
        {
            return (false, $"Lỗi ({ex.GetType().Name})");
        }
    }

    /// <summary>
    /// EN: Login via BFF — sends email/password to BFF; BFF exchanges with IS4 using server-side
    ///     credentials and sets an httpOnly session cookie. WASM never sees the access token.
    /// VI: Đăng nhập qua BFF — gửi email/password đến BFF; BFF trao đổi với IS4 dùng
    ///     server-side credentials và đặt httpOnly session cookie. WASM không thấy access token.
    /// </summary>
    public async Task<(bool Success, string? Error)> LoginAsync(string email, string password, string role = "owner")
    {
        try
        {
            var response = await _http.PostAsJsonAsync("/api/bff/auth/login", new { email, password }, JsonOptions);

            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(json);

                // EN: BFF returns session info (email, role, expiresAt) — not the raw token.
                // VI: BFF trả về thông tin session (email, role, expiresAt) — không phải token thô.
                DateTime? expiresAt = null;
                string? resolvedRole = role;

                if (doc.RootElement.TryGetProperty("data", out var data))
                {
                    if (data.TryGetProperty("expiresAt", out var expEl) && expEl.ValueKind != JsonValueKind.Null)
                        expiresAt = expEl.GetDateTime();
                    if (data.TryGetProperty("role", out var roleEl) && roleEl.ValueKind == JsonValueKind.String)
                    {
                        var serverRole = roleEl.GetString();
                        // EN: Keep the login-flow role (staff/branch) if server returns generic "owner"
                        //     because IAM doesn't distinguish staff vs owner in JWT claims.
                        // VI: Giữ role từ login flow (staff/branch) nếu server trả "owner" chung chung
                        //     vì IAM không phân biệt staff vs owner trong JWT claims.
                        if (role != "owner" && serverRole == "owner")
                            resolvedRole = role;
                        else
                            resolvedRole = serverRole ?? role;
                    }
                    if (data.TryGetProperty("email", out var emailEl) && emailEl.ValueKind == JsonValueKind.String)
                        email = emailEl.GetString() ?? email;
                }

                // EN: Persist non-sensitive metadata for UI via LocalStorageService (FRONT-W-07).
                // VI: Lưu metadata không nhạy cảm cho UI qua LocalStorageService (FRONT-W-07).
                await _localStorage.SetAsync(StoredEmailKey, email);
                await _localStorage.SetAsync(StoredRoleKey, resolvedRole ?? string.Empty);

                _authState.Login(email, resolvedRole, expiresAt);
                return (true, null);
            }

            var errorContent = await response.Content.ReadAsStringAsync();
            try
            {
                using var errDoc = JsonDocument.Parse(errorContent);
                if (errDoc.RootElement.TryGetProperty("message", out var msg))
                    return (false, msg.GetString());
            }
            catch { }

            return (false, $"Đăng nhập thất bại ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            return (false, $"Lỗi kết nối: {ex.Message}");
        }
    }

    /// <summary>
    /// EN: Try to restore session from the BFF session endpoint (reads httpOnly cookie server-side).
    ///     Falls back to localStorage metadata for email/role display only.
    /// VI: Khôi phục session từ BFF session endpoint (đọc httpOnly cookie phía server).
    ///     Fallback sang localStorage metadata chỉ để hiển thị email/role.
    /// </summary>
    public async Task TryRestoreSessionAsync(string role = "owner")
    {
        try
        {
            // EN: Ask BFF whether we have a valid session (reads httpOnly cookie).
            // VI: Hỏi BFF xem có session hợp lệ không (đọc httpOnly cookie).
            var response = await _http.GetAsync("/api/bff/auth/session");
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(json);

                if (doc.RootElement.TryGetProperty("data", out var data))
                {
                    var email = data.TryGetProperty("email", out var eProp) ? eProp.GetString() : null;
                    var serverRole = data.TryGetProperty("role", out var rProp) ? rProp.GetString() : null;
                    // EN: Prefer stored role from localStorage over server "owner" default —
                    //     staff login saves "staff" but JWT always returns "owner".
                    // VI: Ưu tiên role đã lưu từ localStorage hơn server default "owner" —
                    //     staff login lưu "staff" nhưng JWT luôn trả "owner".
                    var storedRole = await _localStorage.GetAsync(StoredRoleKey);
                    var resolvedRole = !string.IsNullOrEmpty(storedRole) && storedRole != "owner" && serverRole == "owner"
                        ? storedRole
                        : serverRole ?? role;
                    DateTime? expiresAt = null;
                    if (data.TryGetProperty("expiresAt", out var expEl) && expEl.ValueKind != JsonValueKind.Null)
                        expiresAt = expEl.GetDateTime();

                    // EN: Fallback to localStorage for email via LocalStorageService (FRONT-W-07).
                    // VI: Fallback localStorage để lấy email qua LocalStorageService (FRONT-W-07).
                    if (string.IsNullOrEmpty(email))
                        email = await _localStorage.GetAsync(StoredEmailKey);

                    if (!string.IsNullOrEmpty(email))
                        _authState.Login(email, resolvedRole ?? role, expiresAt);
                }
            }
            // EN: 401 from BFF means no valid session — this is expected (not an error).
            // VI: 401 từ BFF nghĩa là không có session hợp lệ — đây là bình thường (không phải lỗi).
        }
        catch { /* Network unavailable or pre-render — silently skip */ }
    }

    /// <summary>
    /// EN: Logout — clears the BFF session cookie and local state.
    /// VI: Đăng xuất — xóa BFF session cookie và trạng thái cục bộ.
    /// </summary>
    public async Task LogoutAsync(string role = "owner")
    {
        try
        {
            // EN: Tell BFF to clear the httpOnly session cookie.
            // VI: Yêu cầu BFF xóa httpOnly session cookie.
            await _http.PostAsync("/api/bff/auth/logout", null);
        }
        catch { /* Best-effort: proceed with local logout even if BFF is unavailable */ }

        // EN: Clear non-sensitive localStorage metadata via LocalStorageService (FRONT-W-07).
        // VI: Xóa metadata localStorage không nhạy cảm qua LocalStorageService (FRONT-W-07).
        await _localStorage.RemoveAsync(StoredEmailKey);
        await _localStorage.RemoveAsync(StoredRoleKey);

        _authState.Logout();
    }
}
