using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using RobotNet10.MapEditor.Shared.DTOs.LayoutData;
using RobotNet10.MapEditor.Shared.DTOs.Requests;
using RobotNet10.MapEditor.Shared.DTOs.Responses;
using RobotNet10.MapManager.Services;
namespace RobotNet10.MapManager.Controllers;
///
/// Controller for retrieving complete layout data and merge/split operations
///
[ApiController]
[Route("api/layout-data")]
[Authorize]
public class LayoutDataController(
ILayoutDataService layoutDataService,
ILogger logger) : ControllerBase
{
private readonly ILayoutDataService _layoutDataService = layoutDataService;
private readonly ILogger _logger = logger;
///
/// Get complete layout data for a layout level
/// Returns all nodes, edges, and stations with full nested properties
///
/// Layout level ID
/// Complete layout data
[HttpGet("{layoutLevelId}")]
[ProducesResponseType(typeof(LayoutDataDto), 200)]
public async Task> GetLayoutData(Guid layoutLevelId)
{
try
{
var data = await _layoutDataService.GetLayoutDataAsync(layoutLevelId);
return Ok(data);
}
catch (Exception ex)
{
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error retrieving layout data for level {LevelId}", layoutLevelId);
return StatusCode(500, CreateErrorResponse("Internal server error while retrieving layout data", "INTERNAL_ERROR"));
}
}
///
/// Merge multiple nodes into one node at center position
///
/// Merge nodes request
/// Merge result with merged node, updated edges, and deleted node IDs
[HttpPost("merge-nodes")]
[ProducesResponseType(typeof(MergeNodesResponse), 200)]
[ProducesResponseType(400)]
public async Task> MergeNodes([FromBody] MergeNodesRequest request)
{
try
{
var result = await _layoutDataService.MergeNodesAsync(request);
return Ok(result);
}
catch (InvalidOperationException ex)
{
_logger.LogWarning(ex, "Failed to merge nodes");
return BadRequest(CreateErrorResponse(ex.Message, "VALIDATION_ERROR"));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error merging nodes");
return StatusCode(500, CreateErrorResponse("Internal server error while merging nodes", "INTERNAL_ERROR"));
}
}
///
/// Split a node into multiple nodes (one for each connected edge)
///
/// Split node request
/// Split result with new nodes, updated edges, and deleted node ID
[HttpPost("split-node")]
[ProducesResponseType(typeof(SplitNodeResponse), 200)]
[ProducesResponseType(400)]
public async Task> SplitNode([FromBody] SplitNodeRequest request)
{
try
{
var result = await _layoutDataService.SplitNodeAsync(request);
return Ok(result);
}
catch (InvalidOperationException ex)
{
_logger.LogWarning(ex, "Failed to split node");
return BadRequest(CreateErrorResponse(ex.Message, "VALIDATION_ERROR"));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error splitting node");
return StatusCode(500, CreateErrorResponse("Internal server error while splitting node", "INTERNAL_ERROR"));
}
}
///
/// Save all layout changes (nodes and edges) in a batch operation
/// Uses transaction to ensure atomicity
///
/// Save request with nodes and edges to update
/// Save result with counts and any skipped items
[HttpPost("save")]
[ProducesResponseType(typeof(SaveLayoutDataResponse), 200)]
[ProducesResponseType(400)]
public async Task> SaveLayoutData([FromBody] SaveLayoutDataRequest request)
{
try
{
var result = await _layoutDataService.SaveLayoutDataAsync(request);
if (!result.Success)
{
return BadRequest(result);
}
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error saving layout data");
return StatusCode(500, CreateErrorResponse("Internal server error while saving layout data", "INTERNAL_ERROR"));
}
}
///
/// Copy selected nodes and edges with an offset
/// Creates new nodes and edges at offset positions
///
/// Copy request with node IDs, edge IDs, and offset
/// Copy result with newly created nodes and edges
[HttpPost("copy-nodes")]
[ProducesResponseType(typeof(CopyNodesResponse), 200)]
[ProducesResponseType(400)]
public async Task> CopyNodes([FromBody] CopyNodesRequest request)
{
try
{
var result = await _layoutDataService.CopyNodesAsync(request);
if (!result.Success)
{
return BadRequest(result);
}
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error copying nodes");
return StatusCode(500, CreateErrorResponse("Internal server error while copying nodes", "INTERNAL_ERROR"));
}
}
private static ErrorResponseDto CreateErrorResponse(
string error,
string? errorCode = null,
Dictionary? details = null)
{
return new ErrorResponseDto
{
Error = error,
ErrorCode = errorCode,
Details = details
};
}
}