using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using DysonNetwork.Drive.Interfaces; using DysonNetwork.Drive.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace DysonNetwork.Drive.Controllers { [ApiController] [Route("api/references")] [Authorize] public class FileReferenceController : ControllerBase { private readonly IFileReferenceService _referenceService; private readonly ILogger _logger; public FileReferenceController( IFileReferenceService referenceService, ILogger logger) { _referenceService = referenceService ?? throw new ArgumentNullException(nameof(referenceService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } [HttpPost] [ProducesResponseType(typeof(CloudFileReference), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task CreateReference([FromBody] CreateReferenceRequest request, CancellationToken cancellationToken = default) { if (!ModelState.IsValid) { return BadRequest(ModelState); } try { var reference = await _referenceService.CreateReferenceAsync( request.FileId, request.ResourceId, request.ResourceType, request.ReferenceType, request.ReferenceId, request.ReferenceName, request.ReferenceMimeType, request.ReferenceSize, request.ReferenceUrl, request.ReferenceThumbnailUrl, request.ReferencePreviewUrl, request.ReferenceMetadata, request.Metadata, cancellationToken); return CreatedAtAction( nameof(GetReference), new { referenceId = reference.Id }, reference); } catch (Exception ex) { _logger.LogError(ex, "Error creating file reference for file {FileId}", request.FileId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while creating the file reference." }); } } [HttpGet("{referenceId}")] [ProducesResponseType(typeof(CloudFileReference), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetReference(Guid referenceId, CancellationToken cancellationToken = default) { try { var reference = await _referenceService.GetReferenceAsync(referenceId, cancellationToken); return Ok(reference); } catch (KeyNotFoundException ex) { _logger.LogWarning(ex, "Reference not found: {ReferenceId}", referenceId); return NotFound(new { message = $"Reference with ID {referenceId} not found." }); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving reference: {ReferenceId}", referenceId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while retrieving the reference." }); } } [HttpGet("file/{fileId}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task GetReferencesForFile(Guid fileId, CancellationToken cancellationToken = default) { try { var references = await _referenceService.GetReferencesForFileAsync(fileId, cancellationToken); return Ok(references); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving references for file: {FileId}", fileId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while retrieving references for the file." }); } } [HttpGet("resource/{resourceType}/{resourceId}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task GetReferencesForResource( string resourceType, string resourceId, CancellationToken cancellationToken = default) { try { var references = await _referenceService.GetReferencesForResourceAsync(resourceId, resourceType, cancellationToken); return Ok(references); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving references for resource: {ResourceType}/{ResourceId}", resourceType, resourceId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while retrieving references for the resource." }); } } [HttpGet("type/{referenceType}")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task GetReferencesOfType(string referenceType, CancellationToken cancellationToken = default) { try { var references = await _referenceService.GetReferencesOfTypeAsync(referenceType, cancellationToken); return Ok(references); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving references of type: {ReferenceType}", referenceType); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while retrieving references of the specified type." }); } } [HttpDelete("{referenceId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DeleteReference(Guid referenceId, CancellationToken cancellationToken = default) { try { var deleted = await _referenceService.DeleteReferenceAsync(referenceId, cancellationToken); if (!deleted) { return NotFound(new { message = $"Reference with ID {referenceId} not found." }); } return NoContent(); } catch (Exception ex) { _logger.LogError(ex, "Error deleting reference: {ReferenceId}", referenceId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while deleting the reference." }); } } [HttpDelete("file/{fileId}")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task DeleteReferencesForFile(Guid fileId, CancellationToken cancellationToken = default) { try { var count = await _referenceService.DeleteReferencesForFileAsync(fileId, cancellationToken); return Ok(new { count }); } catch (Exception ex) { _logger.LogError(ex, "Error deleting references for file: {FileId}", fileId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while deleting references for the file." }); } } [HttpDelete("resource/{resourceType}/{resourceId}")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task DeleteReferencesForResource( string resourceType, string resourceId, CancellationToken cancellationToken = default) { try { var count = await _referenceService.DeleteReferencesForResourceAsync(resourceId, resourceType, cancellationToken); return Ok(new { count }); } catch (Exception ex) { _logger.LogError(ex, "Error deleting references for resource: {ResourceType}/{ResourceId}", resourceType, resourceId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while deleting references for the resource." }); } } [HttpPut("{referenceId}/metadata")] [ProducesResponseType(typeof(CloudFileReference), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateReferenceMetadata( Guid referenceId, [FromBody] Dictionary metadata, CancellationToken cancellationToken = default) { if (metadata == null || metadata.Count == 0) { return BadRequest(new { message = "No metadata provided." }); } try { var reference = await _referenceService.UpdateReferenceMetadataAsync(referenceId, metadata, cancellationToken); return Ok(reference); } catch (KeyNotFoundException ex) { _logger.LogWarning(ex, "Reference not found: {ReferenceId}", referenceId); return NotFound(new { message = $"Reference with ID {referenceId} not found." }); } catch (Exception ex) { _logger.LogError(ex, "Error updating reference metadata: {ReferenceId}", referenceId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while updating the reference metadata." }); } } [HttpPut("{referenceId}/resource")] [ProducesResponseType(typeof(CloudFileReference), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateReferenceResource( Guid referenceId, [FromBody] UpdateReferenceResourceRequest request, CancellationToken cancellationToken = default) { if (!ModelState.IsValid) { return BadRequest(ModelState); } try { var reference = await _referenceService.UpdateReferenceResourceAsync( referenceId, request.NewResourceId, request.NewResourceType, cancellationToken); return Ok(reference); } catch (KeyNotFoundException ex) { _logger.LogWarning(ex, "Reference not found: {ReferenceId}", referenceId); return NotFound(new { message = $"Reference with ID {referenceId} not found." }); } catch (Exception ex) { _logger.LogError(ex, "Error updating reference resource: {ReferenceId}", referenceId); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while updating the reference resource." }); } } [HttpGet("exists/{fileId}/{resourceType}/{resourceId}")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] public async Task HasReference( Guid fileId, string resourceType, string resourceId, [FromQuery] string? referenceType = null, CancellationToken cancellationToken = default) { try { var exists = await _referenceService.HasReferenceAsync( fileId, resourceId, resourceType, referenceType, cancellationToken); return Ok(new { exists }); } catch (Exception ex) { _logger.LogError( ex, "Error checking reference existence - File: {FileId}, Resource: {ResourceType}/{ResourceId}, ReferenceType: {ReferenceType}", fileId, resourceType, resourceId, referenceType); return StatusCode(StatusCodes.Status500InternalServerError, new { message = "An error occurred while checking reference existence." }); } } } public class CreateReferenceRequest { public Guid FileId { get; set; } public string ResourceId { get; set; } = null!; public string ResourceType { get; set; } = null!; public string ReferenceType { get; set; } = null!; public string? ReferenceId { get; set; } public string? ReferenceName { get; set; } public string? ReferenceMimeType { get; set; } public long? ReferenceSize { get; set; } public string? ReferenceUrl { get; set; } public string? ReferenceThumbnailUrl { get; set; } public string? ReferencePreviewUrl { get; set; } public string? ReferenceMetadata { get; set; } public Dictionary? Metadata { get; set; } } public class UpdateReferenceResourceRequest { public string NewResourceId { get; set; } = null!; public string NewResourceType { get; set; } = null!; } }