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

namespace planli.backend.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    [Authorize] // requires valid JWT for all endpoints in this controller

    public class BoardController : BaseController
    {
        private readonly PlanliDbContext _db;
        public BoardController(PlanliDbContext db)
        {
            _db = db;
        }

        // GET /api/boards
        [HttpGet]
        public async Task<IActionResult> GetUserBoards() {
            
            var currentUserId = GetCurrentUserId();

            var boards = await _db.Boards
                .Where(b => b.Members.Any(m => m.UserId == currentUserId))
                .Select(b => new BoardResponseDto
                {
                    Id = b.Id,
                    Name = b.Name,
                    Members = b.Members.Select(m => new BoardMemberResponseDto
                    {
                        UserId = m.UserId,
                        Name = m.User.Name,
                        Email = m.User.Email,
                        Role = m.Role
                    }).ToList()
                })
                .ToListAsync();

            return Ok(boards);
        }

        // GET /api/boards/{id}
        [HttpGet("{id}")]
        public async Task<IActionResult> GetBoard(int id)
        {
            var currentUserId = GetCurrentUserId();

            var board = await _db.Boards
                .Include(b => b.Members)
                .ThenInclude(m => m.User)
                .FirstOrDefaultAsync(b => b.Id == id);

            if (board == null) return NotFound();

            var isMember = board.Members.Any(m => m.UserId == currentUserId);
            if (!isMember) return Forbid();

            return Ok(new BoardResponseDto
            {
                Id = board.Id,
                Name = board.Name,
                Members = board.Members.Select(m => new BoardMemberResponseDto
                {
                    UserId = m.UserId,
                    Name = m.User.Name,
                    Email = m.User.Email,
                    Role = m.Role
                }).ToList()
            });
        }


        // POST /api/boards
        [HttpPost]
        public async Task<IActionResult> CreateBoard(CreateBoardDto dto)
        {
            var currentUserId = GetCurrentUserId();

            var board = new Board
            {
                Name = dto.Name,
                Members = new List<BoardMember>
                {
                    new BoardMember
                    {
                        UserId = currentUserId,
                        Role = BoardRole.Owner,
                        JoinedAt = DateTime.UtcNow
                    }
                }

            };

            _db.Boards.Add(board);
            await _db.SaveChangesAsync();

            var createdBoard = await _db.Boards
                .Include(b => b.Members)
                    .ThenInclude(m => m.User)
                .FirstAsync(b => b.Id == board.Id);

            return CreatedAtAction(nameof(GetBoard), new { id = createdBoard.Id }, new BoardResponseDto
            {
                Id = createdBoard.Id,
                Name = createdBoard.Name,
                Members = createdBoard.Members.Select(m => new BoardMemberResponseDto
                {
                    UserId = m.UserId,
                    Name = m.User.Name,
                    Email = m.User.Email,
                    Role = m.Role
                }).ToList()
            });

        }

        // PUT /api/boards/{id}
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateBoard(int id, UpdateBoardDto dto)
        {
            var board = await _db.Boards.FindAsync(id);
            if (board == null) return NotFound();

            var currentUserId = GetCurrentUserId();
            var isOwner = await _db.BoardMembers
                .AnyAsync(m => m.BoardId == id
                           && m.UserId == currentUserId
                           && m.Role == BoardRole.Owner);

            if (!isOwner) return Forbid();

            board.Name = dto.Name;

            await _db.SaveChangesAsync();
            return NoContent();
        }

        // DELETE /api/boards/{id}
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteBoard(int id)
        {
            var board = await _db.Boards.FindAsync(id);
            if (board == null) return NotFound();

            var currentUserId = GetCurrentUserId();
            var isOwner = await _db.BoardMembers
                .AnyAsync(m => m.BoardId == id
                    && m.UserId == currentUserId
                    && m.Role == BoardRole.Owner);

            if (!isOwner) return Forbid();

            _db.Boards.Remove(board);
            await _db.SaveChangesAsync();
            return NoContent();
        }

        // POST /api/boards/{id}/members
        [HttpPost("{id}/members")]
        public async Task<IActionResult> AddMember(int id, BoardAddMemberDto dto)
        {
            var currentUserId = GetCurrentUserId();

            var currentUserMember = await _db.BoardMembers
                .FirstOrDefaultAsync(m => m.BoardId == id && m.UserId == currentUserId);

            if (currentUserMember == null) return Forbid();

            // viewers cannot add members
            if (currentUserMember.Role == BoardRole.Viewer) return Forbid();

            // editors can only add viewers
            if (currentUserMember.Role == BoardRole.Editor && dto.Role != BoardRole.Viewer)
                return BadRequest("Editors can only add members as Viewer.");

            // nobody can add a member directly as Owner
            if (dto.Role == BoardRole.Owner)
                return BadRequest("Cannot add a member as Owner.");

            var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == dto.Email);
            if (user == null) return NotFound("User not found.");

            var alreadyMember = await _db.BoardMembers
                .AnyAsync(m => m.BoardId == id && m.UserId == user.Id);
            if (alreadyMember) return BadRequest("User is already a member.");

            _db.BoardMembers.Add(new BoardMember
            {
                BoardId = id,
                UserId = user.Id,
                Role = dto.Role,
                JoinedAt = DateTime.UtcNow
            });

            await _db.SaveChangesAsync();
            return NoContent();
        }

        // PUT /api/boards/{id}/members/{userId}
        [HttpPut("{id}/members/{userId}")]
        public async Task<IActionResult> UpdateMember(int id, int userId, BoardUpdateMemberDto dto)
        {
            var currentUserId = GetCurrentUserId();
            var isOwner = await _db.BoardMembers
                .AnyAsync(m => m.BoardId == id
                           && m.UserId == currentUserId
                           && m.Role == BoardRole.Owner);

            if (!isOwner) return Forbid();

            var member = await _db.BoardMembers
                .FirstOrDefaultAsync(m => m.BoardId == id && m.UserId == userId);
            if (member == null) return NotFound();

            // prevent changing the owner's role
            if (member.Role == BoardRole.Owner) return BadRequest("Cannot change the owner's role");

            // if assigning new owner, demote current owner to editor first
            if (dto.Role == BoardRole.Owner)
            {
                var currentOwner = await _db.BoardMembers
                    .FirstAsync(m => m.BoardId == id && m.Role == BoardRole.Owner);
                currentOwner.Role = BoardRole.Editor;
            }

            member.Role = dto.Role;
            await _db.SaveChangesAsync();
            return NoContent();
        }

        // DELETE /api/boards/{id}/members/{userId}
        [HttpDelete("{id}/members/{userId}")]
        public async Task<IActionResult> RemoveMember(int id, int userId)
        {
            var currentUserId = GetCurrentUserId();

            var currentUser = await _db.BoardMembers
                .FirstOrDefaultAsync(m => m.BoardId == id && m.UserId == currentUserId);

            var memberToRemove = await _db.BoardMembers
                .FirstOrDefaultAsync(m => m.BoardId == id && m.UserId == userId);

            if (memberToRemove == null) return NotFound();

            // prevent removing the owner
            if (memberToRemove.Role == BoardRole.Owner) return BadRequest("Cannot remove the owner.");

            // viewers can only remove themselves
            if (currentUser.Role == BoardRole.Viewer && currentUserId != userId) return Forbid();
            // editors can only remove viewers or themselves
            if (currentUser.Role == BoardRole.Editor && memberToRemove.Role != BoardRole.Viewer && currentUserId != userId) return Forbid();

            _db.BoardMembers.Remove(memberToRemove);
            await _db.SaveChangesAsync();
            return NoContent();
        }
    }
}
