using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using StorageService.Domain.AggregatesModel.FolderAggregate;
using Swashbuckle.AspNetCore.Annotations;
using System.Security.Claims;

namespace StorageService.API.Controllers;

/// <summary>
/// EN: Controller for folder operations.
/// VI: Controller cho các thao tác folder.
/// </summary>
/// <remarks>
/// EN: CRITICAL: Folders are LOGICAL only. Storage uses flat UUID keys.
/// VI: QUAN TRỌNG: Folders chỉ là LOGIC. Storage dùng flat UUID keys.
/// </remarks>
[ApiController]
[Route("api/v1/storage/folders")]
[Authorize]
[Produces("application/json")]
public class FoldersController : ControllerBase
{
    private readonly IFolderRepository _folderRepository;
    private readonly ILogger<FoldersController> _logger;

    public FoldersController(
        IFolderRepository folderRepository,
        ILogger<FoldersController> logger)
    {
        _folderRepository = folderRepository;
        _logger = logger;
    }

    /// <summary>
    /// EN: Create a new folder.
    /// VI: Tạo folder mới.
    /// </summary>
    [HttpPost]
    [SwaggerOperation(Summary = "Create folder", Description = "Create a new folder")]
    [SwaggerResponse(201, "Folder created", typeof(ApiResponse<FolderDto>))]
    [SwaggerResponse(400, "Bad request")]
    [SwaggerResponse(409, "Folder already exists")]
    public async Task<ActionResult<ApiResponse<FolderDto>>> CreateFolder([FromBody] CreateFolderRequest request)
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId))
        {
            return Unauthorized(new ApiResponse<FolderDto> { Success = false, Error = "User ID not found" });
        }

        // EN: Check if folder name already exists / VI: Kiểm tra tên folder đã tồn tại
        if (await _folderRepository.ExistsAsync(userId, request.ParentId, request.Name))
        {
            return Conflict(new ApiResponse<FolderDto> 
            { 
                Success = false, 
                Error = "A folder with this name already exists in the selected location" 
            });
        }

        Folder folder;
        if (request.ParentId == null)
        {
            folder = Folder.CreateRoot(userId, request.Name);
        }
        else
        {
            var parent = await _folderRepository.GetByIdAsync(request.ParentId.Value);
            if (parent == null || parent.UserId != userId)
            {
                return NotFound(new ApiResponse<FolderDto> { Success = false, Error = "Parent folder not found" });
            }
            folder = parent.CreateChild(request.Name);
        }

        await _folderRepository.AddAsync(folder);
        await _folderRepository.UnitOfWork.SaveEntitiesAsync();

        _logger.LogInformation("Folder created: {FolderId} by user {UserId}", folder.Id, userId);

        return CreatedAtAction(
            nameof(GetFolder),
            new { folderId = folder.Id },
            new ApiResponse<FolderDto> { Success = true, Data = ToDto(folder) });
    }

    /// <summary>
    /// EN: Get all root folders.
    /// VI: Lấy tất cả root folders.
    /// </summary>
    [HttpGet]
    [SwaggerOperation(Summary = "Get folders", Description = "Get all root folders or children of a parent")]
    [SwaggerResponse(200, "Folders retrieved", typeof(ApiResponse<IEnumerable<FolderDto>>))]
    public async Task<ActionResult<ApiResponse<IEnumerable<FolderDto>>>> GetFolders([FromQuery] Guid? parentId = null)
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId))
        {
            return Unauthorized(new ApiResponse<IEnumerable<FolderDto>> { Success = false, Error = "User ID not found" });
        }

        IEnumerable<Folder> folders;
        if (parentId == null)
        {
            folders = await _folderRepository.GetRootFoldersAsync(userId);
        }
        else
        {
            var parent = await _folderRepository.GetByIdAsync(parentId.Value);
            if (parent == null || parent.UserId != userId)
            {
                return NotFound(new ApiResponse<IEnumerable<FolderDto>> { Success = false, Error = "Parent folder not found" });
            }
            folders = await _folderRepository.GetChildFoldersAsync(parentId.Value);
        }

        return Ok(new ApiResponse<IEnumerable<FolderDto>>
        {
            Success = true,
            Data = folders.Select(ToDto)
        });
    }

    /// <summary>
    /// EN: Get folder by ID.
    /// VI: Lấy folder theo ID.
    /// </summary>
    [HttpGet("{folderId:guid}")]
    [SwaggerOperation(Summary = "Get folder", Description = "Get folder details by ID")]
    [SwaggerResponse(200, "Folder retrieved", typeof(ApiResponse<FolderDto>))]
    [SwaggerResponse(404, "Folder not found")]
    public async Task<ActionResult<ApiResponse<FolderDto>>> GetFolder([FromRoute] Guid folderId)
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId))
        {
            return Unauthorized(new ApiResponse<FolderDto> { Success = false, Error = "User ID not found" });
        }

        var folder = await _folderRepository.GetByIdAsync(folderId);
        if (folder == null || folder.UserId != userId)
        {
            return NotFound(new ApiResponse<FolderDto> { Success = false, Error = "Folder not found" });
        }

        return Ok(new ApiResponse<FolderDto> { Success = true, Data = ToDto(folder) });
    }

    /// <summary>
    /// EN: Rename folder. O(1) operation.
    /// VI: Đổi tên folder. O(1).
    /// </summary>
    [HttpPut("{folderId:guid}")]
    [SwaggerOperation(Summary = "Rename folder", Description = "Rename a folder (O(1) operation - database only)")]
    [SwaggerResponse(200, "Folder renamed", typeof(ApiResponse<FolderDto>))]
    [SwaggerResponse(404, "Folder not found")]
    [SwaggerResponse(409, "Folder name already exists")]
    public async Task<ActionResult<ApiResponse<FolderDto>>> RenameFolder(
        [FromRoute] Guid folderId,
        [FromBody] RenameFolderRequest request)
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId))
        {
            return Unauthorized(new ApiResponse<FolderDto> { Success = false, Error = "User ID not found" });
        }

        var folder = await _folderRepository.GetByIdAsync(folderId);
        if (folder == null || folder.UserId != userId)
        {
            return NotFound(new ApiResponse<FolderDto> { Success = false, Error = "Folder not found" });
        }

        // EN: Check if new name already exists / VI: Kiểm tra tên mới đã tồn tại
        if (folder.Name != request.Name && 
            await _folderRepository.ExistsAsync(userId, folder.ParentId, request.Name))
        {
            return Conflict(new ApiResponse<FolderDto> 
            { 
                Success = false, 
                Error = "A folder with this name already exists" 
            });
        }

        folder.Rename(request.Name);
        _folderRepository.Update(folder);
        await _folderRepository.UnitOfWork.SaveEntitiesAsync();

        _logger.LogInformation("Folder renamed: {FolderId} to {NewName}", folderId, request.Name);

        return Ok(new ApiResponse<FolderDto> { Success = true, Data = ToDto(folder) });
    }

    /// <summary>
    /// EN: Delete folder.
    /// VI: Xóa folder.
    /// </summary>
    [HttpDelete("{folderId:guid}")]
    [SwaggerOperation(Summary = "Delete folder", Description = "Soft delete a folder")]
    [SwaggerResponse(204, "Folder deleted")]
    [SwaggerResponse(404, "Folder not found")]
    public async Task<IActionResult> DeleteFolder([FromRoute] Guid folderId)
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId))
        {
            return Unauthorized(new ApiResponse<object> { Success = false, Error = "User ID not found" });
        }

        var folder = await _folderRepository.GetByIdAsync(folderId);
        if (folder == null || folder.UserId != userId)
        {
            return NotFound(new ApiResponse<object> { Success = false, Error = "Folder not found" });
        }

        folder.Delete();
        _folderRepository.Update(folder);
        await _folderRepository.UnitOfWork.SaveEntitiesAsync();

        _logger.LogInformation("Folder deleted: {FolderId}", folderId);

        return NoContent();
    }

    private static FolderDto ToDto(Folder folder) => new(
        folder.Id,
        folder.ParentId,
        folder.Name,
        folder.Path,
        folder.Level,
        folder.CreatedAt,
        folder.UpdatedAt);
}

#region DTOs

/// <summary>EN: Folder DTO / VI: DTO Folder</summary>
public record FolderDto(
    Guid Id,
    Guid? ParentId,
    string Name,
    string Path,
    int Level,
    DateTime CreatedAt,
    DateTime UpdatedAt);

/// <summary>EN: Request to create folder / VI: Request tạo folder</summary>
public record CreateFolderRequest(string Name, Guid? ParentId = null);

/// <summary>EN: Request to rename folder / VI: Request đổi tên folder</summary>
public record RenameFolderRequest(string Name);

#endregion
