using bdAntifraude.db;
using bdAntifraude.dbcontext;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using SwaggerAntifraude.DTOs;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace SwaggerAntifraude.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly IConfiguration _configuration;

        public AuthController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [AllowAnonymous]
        [HttpPost("login")]
        public IActionResult Login([FromBody] LoginDto loginDto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            var (token, persona) = AuthenticateUser(loginDto);

            if (token == null)
                return Unauthorized("Nombre de usuario o contraseña incorrectos.");

            if (token.Equals("no autorizado"))
            {
                return Unauthorized("Usuario no autorizado");
            }
            return Ok(new
            {
                Token = token,
                User = new
                {
                    persona.NIF,
                    persona.NOMBRE,
                    persona.APELLIDOS,
                    persona.ADMINISTRARPTYREGISTRO
                }
            });
        }

        [AllowAnonymous]
        [HttpGet("login-cert")]
        public IActionResult LoginWithCertificateGet([FromQuery] bool iframe = false)
        {
            // Obtener el certificado enviado durante el handshake TLS
            var clientCert = HttpContext.Connection.ClientCertificate;
            if (clientCert == null)
                return Unauthorized("Certificado no proporcionado.");

            // Extraer el DNI desde el certificado
            var dni = ObtenerDNI(clientCert);
            if (string.IsNullOrEmpty(dni))
                return Unauthorized("No se pudo obtener un DNI válido del certificado.");

            using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: true))
            {
                // Mapeo de ejemplo (como en tu código)
                if (dni == "25621303Q" || dni == "52234173T" ||dni == "47201739N")
                    dni = "44286377S";

                var persona = context.PERSONAS.FirstOrDefault(p => p.NIF == dni);
                if (persona == null)
                    return Unauthorized("Usuario no encontrado en la base de datos.");

                // Generar token JWT
                var jwtSettings = _configuration.GetSection("Jwt");
                var key = Encoding.UTF8.GetBytes(jwtSettings["Key"]);
                var claims = new[]
                {
                    new Claim(JwtRegisteredClaimNames.Sub, persona.NIF),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(ClaimTypes.Role, persona.ADMINISTRARPTYREGISTRO == true ? "Supervisor" : "Lectura")
                };



                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(claims),
                    Expires = DateTime.UtcNow.AddMinutes(double.Parse(jwtSettings["ExpiresInMinutes"])),
                    Issuer = jwtSettings["Issuer"],
                    Audience = jwtSettings["Audience"],
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };

                var tokenHandler = new JwtSecurityTokenHandler();
                var token = tokenHandler.CreateToken(tokenDescriptor);
                var jwtToken = tokenHandler.WriteToken(token);

                if (iframe)
                {
                    // Generar una página HTML mínima que envíe el token y datos del usuario mediante postMessage al contexto padre
                    var html = $@"
<html>
  <head><title>Autenticación Certificado</title></head>
  <body>
    <script>
      window.parent.postMessage({{
          token: '{jwtToken}',
          user: {{
              NIF: '{persona.NIF}',
              NOMBRE: '{persona.NOMBRE}',
              APELLIDOS: '{persona.APELLIDOS}',
              ADMINISTRARPTYREGISTRO: {persona.ADMINISTRARPTYREGISTRO.ToString().ToLower()}
          }}
      }}, '*');
    </script>
  </body>
</html>";
                    return Content(html, "text/html; charset=utf-8");
                }
                else
                {
                    return Ok(new
                    {
                        Token = jwtToken,
                        User = new
                        {
                            persona.NIF,
                            persona.NOMBRE,
                            persona.APELLIDOS,
                            persona.ADMINISTRARPTYREGISTRO
                        }
                    });
                }
            }
        }
        private string ObtenerDNI(X509Certificate2 certificado)
        {
            try
            {
                // Extraer el Common Name (CN) del Subject
                var subject = certificado.Subject;
                var cnPrefix = "CN=";
                var startIndex = subject.IndexOf(cnPrefix, StringComparison.OrdinalIgnoreCase);
                if (startIndex == -1)
                    return string.Empty;

                startIndex += cnPrefix.Length;
                var endIndex = subject.IndexOf(',', startIndex);
                if (endIndex == -1) endIndex = subject.Length;

                // Extraer el CN
                var commonName = subject.Substring(startIndex, endIndex - startIndex).Trim();

                // Extraer el DNI del CN usando una expresión regular
                var dniMatch = System.Text.RegularExpressions.Regex.Match(commonName, @"\b\d{8}[A-Z]\b");
                if (dniMatch.Success)
                {
                    return dniMatch.Value; // Devuelve solo el DNI
                }

                return string.Empty; // No se encontró un DNI válido
            }
            catch
            {
                return string.Empty;
            }
        }



        private (string token, PERSONAS persona) AuthenticateUser(LoginDto loginDto)
        {
            using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: true))
            {
                var persona = context.PERSONAS.FirstOrDefault(p => p.NIF == loginDto.NombreUsuario);

                if (persona == null || loginDto.Contraseña != "OficinaAntifraude.Tecnosis")
                {
                    return (null, null);
                }

                var jwtSettings = _configuration.GetSection("Jwt");
                var key = Encoding.UTF8.GetBytes(jwtSettings["Key"]);

                var claims = new[]
                {
                    new Claim(JwtRegisteredClaimNames.Sub, persona.NIF),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(ClaimTypes.Role, persona.ADMINISTRARPTYREGISTRO == true ? "Supervisor" : "Lectura")
                };
                if (loginDto.Origen.Equals("Registro") && persona.ADMINISTRARPTYREGISTRO == false)
                {
                    return("no autorizado", null);
                }

                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(claims),
                    Expires = DateTime.UtcNow.AddMinutes(double.Parse(jwtSettings["ExpiresInMinutes"])),
                    Issuer = jwtSettings["Issuer"],
                    Audience = jwtSettings["Audience"],
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };

                var tokenHandler = new JwtSecurityTokenHandler();
                var token = tokenHandler.CreateToken(tokenDescriptor);
                var jwtToken = tokenHandler.WriteToken(token);

                return (jwtToken, persona);
            }
        }


    }
}
