using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using RobotNet10.MapEditor.Shared.DTOs.Responses;
using RobotNet10.MapManager.Services;
namespace RobotNet10.MapManager.Controllers;
///
/// Controller for managing background images for layout levels
///
[ApiController]
[Route("api/images")]
[Authorize]
public class ImagesController(
IImageStorageService imageStorageService,
ILogger logger) : ControllerBase
{
private readonly IImageStorageService _imageStorageService = imageStorageService;
private readonly ILogger _logger = logger;
///
/// Get background image for a layout level
///
/// Layout level ID
/// Image file (PNG)
[HttpGet("layout/{layoutLevelId}")]
[ProducesResponseType(typeof(FileStreamResult), 200)]
[ProducesResponseType(404)]
public async Task GetLayoutImage(Guid layoutLevelId)
{
try
{
var imageStream = await _imageStorageService.GetImageAsync(layoutLevelId);
if (imageStream == null)
{
if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning("Image not found for layout level {LevelId}", layoutLevelId);
return NotFound(CreateErrorResponse($"Image not found for layout level '{layoutLevelId}'", "IMAGE_NOT_FOUND"));
}
return File(imageStream, "image/png", $"{layoutLevelId}.png");
}
catch (Exception ex)
{
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error retrieving image for layout level {LevelId}", layoutLevelId);
return StatusCode(500, CreateErrorResponse("Internal server error while retrieving image", "INTERNAL_ERROR"));
}
}
///
/// Upload or replace background image for a layout level
///
/// Layout level ID
/// Image file (PNG format)
/// Success message
[HttpPost("layout/{layoutLevelId}")]
[ProducesResponseType(200)]
[ProducesResponseType(400)]
public async Task UploadLayoutImage(Guid layoutLevelId, IFormFile file)
{
// Validate file
if (file == null || file.Length == 0)
{
return BadRequest(CreateErrorResponse("No file provided", "VALIDATION_ERROR"));
}
// Validate file type
if (!file.ContentType.Equals("image/png", StringComparison.OrdinalIgnoreCase))
{
return BadRequest(CreateErrorResponse("Only PNG images are supported", "VALIDATION_ERROR"));
}
// Validate file size (max 10MB)
const long maxFileSize = 10 * 1024 * 1024; // 10MB
if (file.Length > maxFileSize)
{
return BadRequest(CreateErrorResponse($"File size exceeds maximum of {maxFileSize / 1024 / 1024}MB", "VALIDATION_ERROR"));
}
try
{
using var stream = file.OpenReadStream();
await _imageStorageService.SaveImageAsync(layoutLevelId, stream);
return Ok(new
{
message = "Image uploaded successfully",
layoutLevelId,
fileName = $"{layoutLevelId}.png",
size = file.Length
});
}
catch (Exception ex)
{
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error uploading image for layout level {LevelId}", layoutLevelId);
return StatusCode(500, CreateErrorResponse("Internal server error while uploading image", "INTERNAL_ERROR"));
}
}
///
/// Delete background image for a layout level
///
/// Layout level ID
/// No content on success
[HttpDelete("layout/{layoutLevelId}")]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
public async Task DeleteLayoutImage(Guid layoutLevelId)
{
try
{
var deleted = await _imageStorageService.DeleteImageAsync(layoutLevelId);
if (!deleted)
{
if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning("Image not found for deletion: {LevelId}", layoutLevelId);
return NotFound(CreateErrorResponse($"Image not found for layout level '{layoutLevelId}'", "IMAGE_NOT_FOUND"));
}
return NoContent();
}
catch (Exception ex)
{
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error deleting image for layout level {LevelId}", layoutLevelId);
return StatusCode(500, CreateErrorResponse("Internal server error while deleting image", "INTERNAL_ERROR"));
}
}
private static ErrorResponseDto CreateErrorResponse(
string error,
string? errorCode = null,
Dictionary? details = null)
{
return new ErrorResponseDto
{
Error = error,
ErrorCode = errorCode,
Details = details
};
}
}