using Microsoft.AspNetCore.Mvc;
using System.Text;
using System.Text.Json.Serialization;
using Yuna.Website.Server.API.DTO;
using Yuna.Website.Server.Infrastructure;
using Yuna.Website.Server.Services.OpenAuthService;
using Yuna.Website.Server.Services.UserService;
using Yuna.Website.Server.Storage.Repositories.UserBindings;

namespace Yuna.Website.Server.API
{
    public class OpenAuthEndpoints
    {
        public void Define(WebApplication app)
        {
            app.MapGet("~/.well-known/openid-configuration", GetConfiguration)
                .WithTags("oauth");

            app.MapMethods("/v1.0", ["HEAD"], Ping)
            .WithTags("oauth");

            app.MapGet("api/oauth/login", LoginViaOauth)
                .WithTags("oauth");

            app.MapPost("api/oauth/auth", AuthorizeViaOauth)
                .WithTags("oauth");

            app.MapPost("/api/oauth/token", GetTokenByCode)
                .WithTags("oauth");
        }

        public class DiscoveryResponse
        {
            [JsonPropertyName("issuer")]
            public string? Issuer { get; set; }

            [JsonPropertyName("authorization_endpoint")]
            public string? AuthorizationEndpoint { get; set; }

            [JsonPropertyName("token_endpoint")]
            public string? TokenEndpoint { get; set; }

            [JsonPropertyName("token_endpoint_auth_methods_supported")]
            public IList<string>? TokenEndpointAuthMethodsSupported { get; set; }

            [JsonPropertyName("token_endpoint_auth_signing_alg_values_supported")]
            public IList<string>? TokenEndpointAuthSigningAlgValuesSupported { get; set; }

            [JsonPropertyName("userinfo_endpoint")]
            public string? UserinfoEndpoint { get; set; }

            [JsonPropertyName("check_session_iframe")]
            public string? CheckSessionIframe { get; set; }

            [JsonPropertyName("end_session_endpoint")]
            public string? EndSessionEndpoint { get; set; }

            [JsonPropertyName("jwks_uri")]
            public string? JwksUri { get; set; }

            [JsonPropertyName("registration_endpoint")]
            public string? RegistrationEndpoint { get; set; }

            [JsonPropertyName("scopes_supported")]
            public IList<string>? ScopesSupported { get; set; }

            [JsonPropertyName("response_types_supported")]
            public IList<string>? ResponseTypesSupported { get; set; }

            [JsonPropertyName("acr_values_supported")]
            public IList<string>? AcrValuesSupported { get; set; }

            [JsonPropertyName("subject_types_supported")]
            public IList<string>? SubjectTypesSupported { get; set; }

            [JsonPropertyName("userinfo_signing_alg_values_supported")]
            public IList<string>? UserinfoSigningAlgValuesSupported { get; set; }

            [JsonPropertyName("userinfo_encryption_alg_values_supported")]
            public IList<string>? UserinfoEncryptionAlgValuesSupported { get; set; }

            [JsonPropertyName("userinfo_encryption_enc_values_supported")]
            public IList<string>? UserinfoEncryptionEncValuesSupported { get; set; }

            [JsonPropertyName("id_token_signing_alg_values_supported")]
            public IList<string>? IdTokenSigningAlgValuesSupported { get; set; }

            [JsonPropertyName("id_token_encryption_alg_values_supported")]
            public IList<string>? IdTokenEncryptionAlgValuesSupported { get; set; }

            [JsonPropertyName("id_token_encryption_enc_values_supported")]
            public IList<string>? IdTokenEncryptionEncValuesSupported { get; set; }

            [JsonPropertyName("request_object_signing_alg_values_supported")]
            public IList<string>? RequestObjectSigningAlgValuesSupported { get; set; }

            [JsonPropertyName("display_values_supported")]
            public IList<string>? DisplayValuesSupported { get; set; }

            [JsonPropertyName("claim_types_supported")]
            public IList<string>? ClaimTypesSupported { get; set; }

            [JsonPropertyName("claims_supported")]
            public IList<string>? ClaimsSupported { get; set; }

            [JsonPropertyName("claims_parameter_supported")]
            public bool? ClaimsParameterSupported { get; set; }

            [JsonPropertyName("service_documentation")]
            public string? ServiceDocumentation { get; set; }

            [JsonPropertyName("ui_locales_supported")]
            public IList<string>? UiLocalesSupported { get; set; }
        }

        public IResult GetConfiguration()
        {
            var response = new DiscoveryResponse()
            {
                Issuer = Settings.HttpsExternalUrl,
                AuthorizationEndpoint = Settings.HttpsExternalUrl + "/makaka",
                TokenEndpoint = Settings.HttpsExternalUrl + "/token",
                TokenEndpointAuthMethodsSupported = ["client_secret_basic", "private_key_jwt"],
                TokenEndpointAuthSigningAlgValuesSupported = ["RS256", "ES256"],

                AcrValuesSupported = ["urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"],
                ResponseTypesSupported = ["code", "code id_token", "id_token", "token id_token"],
                SubjectTypesSupported = [],

                UserinfoEncryptionEncValuesSupported = ["A128CBC-HS256", "A128GCM"],
                IdTokenSigningAlgValuesSupported = ["RS256", "ES256", "HS256"],
                IdTokenEncryptionAlgValuesSupported = ["RSA1_5", "A128KW"],
                IdTokenEncryptionEncValuesSupported = ["A128CBC-HS256", "A128GCM"],

                RequestObjectSigningAlgValuesSupported = ["none", "RS256", "ES256"],
                DisplayValuesSupported = ["page", "popup"],
                ClaimTypesSupported = ["normal", "distributed"],


                ScopesSupported = [],
                ClaimsSupported = [],
                ClaimsParameterSupported = false,
                ServiceDocumentation = null,
                UiLocalesSupported = ["ru-RU"],

            };

            return Results.Json(response);
        }

        public IResult Ping()
        {
            return Results.Ok();
        }

        public IResult LoginViaOauth(
            [FromQuery] string state,
            [FromQuery] string redirect_uri,
            [FromQuery] string response_type,
            [FromQuery] string client_id, 
            HttpContext context,
            IOpenAuthService openAuthService)
        {

            var host = context.Request.Host;
            if (!openAuthService.ValidateLoginRequest(response_type, client_id, host.Value))
                return Results.Unauthorized();

            //TODO LOGIN PAGE URL IN SETTINGS
            return Results.Redirect($"https://localhost:5173/login?redirect_to={redirect_uri}&client_id={client_id}&state={state}");
        }


        public class OauthLoginRequest : LoginRequest
        {
            [JsonPropertyName("state")]
            public string State { get; set; } = null!;

            [JsonPropertyName("redirectUri")]
            public string RedirectUri { get; set; } = null!;
        }
        public async Task<IResult> AuthorizeViaOauth([FromBody] OauthLoginRequest request, IUserService userService, IOpenAuthService openAuthService, HttpContext context)
        {
            var user = await userService.GetByUsername(request.UserName);

            if (user is null) return Results.Problem(statusCode: 401, detail:"invalid user input data");

            var inputHashedPassword = Encrypter.HashPassword(request.RawPassword, user.UserName);

            if (!inputHashedPassword.Equals(user.HashedPassword)) return Results.Problem(statusCode: 401, detail: "invalid login attempt");

            var binding = openAuthService.CreateBinding(user);

            if(binding is null) return Results.Problem(statusCode: 401, detail: "Unable to create binding");

            return Results.Ok(request.RedirectUri + $"?code={binding.Code}&state={request.State}");
        }


        public class OauthTokenResponse
        {
            [JsonPropertyName("access_token")]
            public string AccessToken { get; set; } = null!;
            
            [JsonPropertyName("token_type")]
            public string TokenType => "Bearer";

            [JsonPropertyName("expires_in")]
            public int ExpiresIn => int.MaxValue;
        }

        public class TokenRequest
        {
            public string Code { get; set; }
            public string ClientId { get; set; } = null!;

            public string ClientSecret { get; set; } = null!;
            
            public string GrantType { get; set; } = null!;

            public string RedirectUri { get; set; } = null!;
        }

        public async Task<IResult> GetTokenByCode(
            HttpContext context,
            ILogger<OpenAuthEndpoints> logger,
            IOpenAuthService openAuthService)
        {
            var request = context.Request;
            var sb = new StringBuilder();
            // Записываем метод и URL запроса
            sb.AppendLine($"{request.Method} {request.Path}{request.QueryString}");

            // Записываем заголовки
            sb.AppendLine("Headers:");
            foreach (var header in request.Headers)
            {
                sb.AppendLine($"{header.Key}: {header.Value}");
            }

            // Записываем тело запроса
            string bodyStr = "";
            sb.AppendLine("Body:");
            request.EnableBuffering(); // Для того чтобы можно было прочитать тело несколько раз
            using (var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true))
            {
                var body = await reader.ReadToEndAsync();
                sb.AppendLine(body);
                bodyStr = body;
                request.Body.Position = 0; // Возвращаем позицию в начале потока
            }

            logger.LogInformation(sb.ToString());



            var tokenRequest = ParseQueryStringToTokenRequest(bodyStr);
           

            if (tokenRequest.GrantType != "authorization_code")
            {
                return Results.BadRequest(new { error = "unsupported_grant_type" });
            }

            if(!tokenRequest.ClientId.Equals("vasich_yandex_server"))
            {
                return Results.BadRequest(new { error = "client id invalid" });
            }

            if(!tokenRequest.ClientSecret.Equals("vasich_molodets_sdelal_oauth"))
            {
                return Results.BadRequest(new { error = "client secret invalid" });
            }

            var userBinding = openAuthService.GetByCode(tokenRequest.Code);

            if (userBinding is null) return Results.BadRequest(new { error = "Invalid binding" });

            return Results.Ok(new OauthTokenResponse()
            {
                AccessToken = userBinding.AccessToken
            });

        }

        private static TokenRequest ParseQueryStringToTokenRequest(string queryString)
        {
            var queryParams = queryString.Split('&')
                .Select(param => param.Split('='))
                .ToDictionary(pair => pair[0], pair => Uri.UnescapeDataString(pair[1]));

            return new TokenRequest
            {
                Code = queryParams["code"],
                ClientId = queryParams["client_id"],
                ClientSecret = queryParams["client_secret"],
                GrantType = queryParams["grant_type"],
                RedirectUri = queryParams["redirect_uri"]
            };
        }
    }
}
