using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using BCrypt.Net;
using Microsoft.EntityFrameworkCore;
using planli.backend.DTOs;
using planli.backend.planli.db;
using planli.backend.planli.db.Model;
using planli.backend.Services;
using System.Security.Claims;

namespace planli.backend.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class AuthController : BaseController
    {
        // ── Cookie names ─────────────────────────────────────────────────────────
        private const string AccessCookie  = "planli_access";
        private const string RefreshCookie = "planli_refresh";

        private readonly PlanliDbContext _db;
        private readonly JWTService      _jwt;

        public AuthController(PlanliDbContext db, JWTService jwt)
        {
            _db  = db;
            _jwt = jwt;
        }

        // ── Register ─────────────────────────────────────────────────────────────
        [HttpPost("register")]
        [AllowAnonymous]
        public async Task<IActionResult> Register(UserRegisterDto dto)
        {
            if (await _db.Users.AnyAsync(u => u.Email == dto.Email))
                return BadRequest("Email already in use.");

            var user = new User
            {
                Name     = dto.Name,
                Email    = dto.Email,
                Password = BCrypt.Net.BCrypt.HashPassword(dto.Password)
            };

            IssueRefreshToken(user);
            _db.Users.Add(user);
            await _db.SaveChangesAsync();

            SetAuthCookies(user);
            return Ok(new MeResponseDto { Id = user.Id, Name = user.Name, Email = user.Email });
        }

        // ── Login ─────────────────────────────────────────────────────────────────
        [HttpPost("login")]
        [AllowAnonymous]
        public async Task<IActionResult> Login(UserLoginDto dto)
        {
            var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == dto.Email);

            if (user == null || !BCrypt.Net.BCrypt.Verify(dto.Password, user.Password))
                return Unauthorized("Invalid email or password.");

            IssueRefreshToken(user);
            await _db.SaveChangesAsync();

            SetAuthCookies(user);
            return Ok(new MeResponseDto { Id = user.Id, Name = user.Name, Email = user.Email });
        }

        // ── Refresh ───────────────────────────────────────────────────────────────
        [HttpPost("refresh")]
        [AllowAnonymous]
        public async Task<IActionResult> Refresh()
        {
            var refreshToken = Request.Cookies[RefreshCookie];
            if (string.IsNullOrWhiteSpace(refreshToken))
                return Unauthorized("No refresh token.");

            var user = await _db.Users
                .FirstOrDefaultAsync(u => u.RefreshToken == refreshToken);

            if (user is null || user.RefreshTokenExpiry < DateTime.UtcNow)
                return Unauthorized("Invalid or expired refresh token.");

            // Rotate: issue new access token + new refresh token
            IssueRefreshToken(user);
            await _db.SaveChangesAsync();

            SetAuthCookies(user);
            return NoContent();
        }

        // ── Logout ────────────────────────────────────────────────────────────────
        [HttpPost("logout")]
        [AllowAnonymous]
        public async Task<IActionResult> Logout()
        {
            var refreshToken = Request.Cookies[RefreshCookie];
            if (!string.IsNullOrWhiteSpace(refreshToken))
            {
                var user = await _db.Users
                    .FirstOrDefaultAsync(u => u.RefreshToken == refreshToken);

                if (user is not null)
                {
                    user.RefreshToken       = null;
                    user.RefreshTokenExpiry = null;
                    await _db.SaveChangesAsync();
                }
            }

            ClearAuthCookies();
            return NoContent();
        }

        // ── Me ────────────────────────────────────────────────────────────────────
        [HttpGet("me")]
        [Authorize]
        public IActionResult Me() => Ok(new MeResponseDto
        {
            Id    = GetCurrentUserId(),
            Name  = User.FindFirstValue(ClaimTypes.Name)  ?? "",
            Email = User.FindFirstValue(ClaimTypes.Email) ?? ""
        });

        // ── Helpers ───────────────────────────────────────────────────────────────
        private static void IssueRefreshToken(User user)
        {
            user.RefreshToken       = JWTService.GenerateRefreshToken();
            user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(30);
        }

        /// <summary>Set HttpOnly access + refresh cookies on the response.</summary>
        private void SetAuthCookies(User user)
        {
            Response.Cookies.Append(AccessCookie, _jwt.GenerateToken(user), new CookieOptions
            {
                HttpOnly = true,
                Secure   = false,           // set true when serving over HTTPS
                SameSite = SameSiteMode.Lax,
                Path     = "/",
                MaxAge   = TimeSpan.FromMinutes(15)
            });

            Response.Cookies.Append(RefreshCookie, user.RefreshToken!, new CookieOptions
            {
                HttpOnly = true,
                Secure   = false,
                SameSite = SameSiteMode.Lax,
                Path     = "/api/Auth",     // only sent to auth endpoints
                MaxAge   = TimeSpan.FromDays(30)
            });
        }

        /// <summary>Expire both cookies so the browser discards them.</summary>
        private void ClearAuthCookies()
        {
            Response.Cookies.Delete(AccessCookie,  new CookieOptions { Path = "/" });
            Response.Cookies.Delete(RefreshCookie, new CookieOptions { Path = "/api/Auth" });
        }
    }
}
