using System.Text.Json;
using Application.Abstractions.YouTube;
using Microsoft.Extensions.Logging;

namespace Infrastructure.Authentication;

internal sealed class GoogleOAuthService(
    HttpClient httpClient,
    ILogger<GoogleOAuthService> logger
) : IGoogleOAuthService
{
    private const string AuthEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
    private const string TokenEndpoint = "https://oauth2.googleapis.com/token";

    // ClientId/Secret은 호출자가 DB Config에서 가져와 전달하거나,
    // ExchangeCodeAsync 내부에서 Config를 조회하도록 확장 가능
    // 현재는 HttpClient DI 시 BaseAddress 없이 사용

    public string GetAuthorizationUrl(string state, string redirectUri, string[] scopes)
    {
        var scope = string.Join(" ", scopes);

        var parameters = new Dictionary<string, string>
        {
            ["response_type"] = "code",
            ["access_type"] = "offline",        // refresh_token 획득을 위해
            ["prompt"] = "consent",              // 항상 동의 화면 (refresh_token 보장)
            ["state"] = state,
            ["redirect_uri"] = redirectUri,
            ["scope"] = scope
        };

        var query = string.Join("&", parameters.Select(kv =>
            $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));

        // client_id는 프론트엔드에서 URL에 추가 (DB Config에서 조회)
        return $"{AuthEndpoint}?{query}";
    }

    public async Task<GoogleOAuthTokens?> ExchangeCodeAsync(string code, string redirectUri, string clientId, string clientSecret, CancellationToken ct)
    {
        try
        {
            var content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["code"] = code,
                ["redirect_uri"] = redirectUri,
                ["grant_type"] = "authorization_code",
                ["client_id"] = clientId,
                ["client_secret"] = clientSecret
            });

            var response = await httpClient.PostAsync(TokenEndpoint, content, ct);

            if (!response.IsSuccessStatusCode)
            {
                var errorBody = await response.Content.ReadAsStringAsync(ct);
                logger.LogWarning("Google OAuth code exchange failed: {StatusCode} {Body}", response.StatusCode, errorBody);
                return null;
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            return ParseTokenResponse(json);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Google OAuth code exchange error");
            return null;
        }
    }

    public async Task<GoogleOAuthTokens?> RefreshTokenAsync(string refreshToken, CancellationToken ct)
    {
        try
        {
            var content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["refresh_token"] = refreshToken,
                ["grant_type"] = "refresh_token"
            });

            var response = await httpClient.PostAsync(TokenEndpoint, content, ct);

            if (!response.IsSuccessStatusCode)
            {
                var errorBody = await response.Content.ReadAsStringAsync(ct);
                logger.LogWarning("Google OAuth token refresh failed: {StatusCode} {Body}", response.StatusCode, errorBody);
                return null;
            }

            var json = await response.Content.ReadAsStringAsync(ct);
            return ParseTokenResponse(json);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Google OAuth token refresh error");
            return null;
        }
    }

    public async Task RevokeTokenAsync(string token, CancellationToken ct)
    {
        try
        {
            var content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["token"] = token
            });

            var response = await httpClient.PostAsync("https://oauth2.googleapis.com/revoke", content, ct);

            if (!response.IsSuccessStatusCode)
            {
                var errorBody = await response.Content.ReadAsStringAsync(ct);
                logger.LogWarning("Google OAuth revoke failed: {StatusCode} {Body}", response.StatusCode, errorBody);
            }
        }
        catch (Exception ex)
        {
            logger.LogWarning(ex, "Google OAuth revoke error (ignored)");
        }
    }

    private static GoogleOAuthTokens? ParseTokenResponse(string json)
    {
        var doc = JsonSerializer.Deserialize<JsonElement>(json);

        if (!doc.TryGetProperty("access_token", out var accessTokenProp))
        {
            return null;
        }

        var accessToken = accessTokenProp.GetString()!;
        var refreshToken = doc.TryGetProperty("refresh_token", out var rtProp) ? rtProp.GetString() : null;
        var expiresIn = doc.TryGetProperty("expires_in", out var expProp) ? expProp.GetInt32() : 3600;
        var scope = doc.TryGetProperty("scope", out var scopeProp) ? scopeProp.GetString() ?? "" : "";

        return new GoogleOAuthTokens(
            accessToken,
            refreshToken,
            DateTime.UtcNow.AddSeconds(expiresIn - 60), // 60초 여유
            scope.Split(' ', StringSplitOptions.RemoveEmptyEntries)
        );
    }
}