using Features.Breweries.Commands.CreateBrewery; using Features.Breweries.Commands.DeleteBrewery; using Features.Breweries.Commands.UpdateBrewery; using Features.Breweries.Dtos; using Features.Breweries.Queries.GetAllBreweries; using Features.Breweries.Queries.GetAllBreweryLocations; using Features.Breweries.Queries.GetBreweryById; using Features.Breweries.Queries.GetBreweryLocationsWithinRange; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Shared.Contracts; namespace Features.Breweries.Controllers; /// Exposes CRUD operations for brewery posts. /// /// Requires JWT authentication by default; and opt out via /// [AllowAnonymous]. /// [ApiController] [Route("api/[controller]")] [Authorize(AuthenticationSchemes = "JWT")] public class BreweryController(IMediator mediator) : ControllerBase { /// Retrieves a single brewery post by ID. /// Allows anonymous access. /// 200 OK with the brewery if found; otherwise 404 Not Found. [AllowAnonymous] [HttpGet("{id:guid}")] public async Task>> GetById(Guid id) { BreweryDto? brewery = await mediator.Send(new GetBreweryByIdQuery(id)); if (brewery is null) return NotFound(new ResponseBody { Message = $"Brewery with ID {id} not found." }); return Ok( new ResponseBody { Message = "Brewery retrieved successfully.", Payload = brewery, } ); } /// Retrieves brewery posts, newest first. /// Maximum number of breweries to return. Unbounded if . /// Number of breweries to skip. Treated as zero if . /// Allows anonymous access. [AllowAnonymous] [HttpGet] public async Task>>> GetAll( [FromQuery] int? limit, [FromQuery] int? offset ) { IEnumerable breweries = await mediator.Send( new GetAllBreweriesQuery(limit, offset) ); return Ok( new ResponseBody> { Message = "Breweries retrieved successfully.", Payload = breweries, } ); } /// Retrieves all brewery posts that have a set location. /// Allows anonymous access. [AllowAnonymous] [HttpGet("locations")] public async Task>>> GetAllLocations() { IEnumerable breweries = await mediator.Send( new GetAllBreweryLocationsQuery() ); return Ok( new ResponseBody> { Message = "Brewery locations retrieved successfully.", Payload = breweries, } ); } /// Retrieves brewery posts with a set location within range of the given coordinates, nearest first. /// The origin latitude, in decimal degrees. /// The origin longitude, in decimal degrees. /// The maximum distance, in metres, from the origin coordinates. /// Allows anonymous access. [AllowAnonymous] [HttpGet("locations/nearby")] public async Task>>> GetLocationsWithinRange( [FromQuery] double latitude, [FromQuery] double longitude, [FromQuery] double rangeInMetres ) { IEnumerable breweries = await mediator.Send( new GetBreweryLocationsWithinRangeQuery(latitude, longitude, rangeInMetres) ); return Ok( new ResponseBody> { Message = "Brewery locations retrieved successfully.", Payload = breweries, } ); } /// Creates a new brewery post. /// 201 Created with the newly created brewery. [HttpPost] public async Task>> Create( [FromBody] CreateBreweryCommand command ) { BreweryDto brewery = await mediator.Send(command); return Created( $"/api/brewery/{brewery.BreweryPostId}", new ResponseBody { Message = "Brewery created successfully.", Payload = brewery, } ); } /// Updates an existing brewery post. /// Must match 's BreweryPostId. /// /// 200 OK with the updated brewery; 400 Bad Request if the route ID does not match the /// payload ID; 404 Not Found if the brewery or its CityId does not exist; or /// 409 Conflict if the brewery was modified since command.RowVersion was read. /// [HttpPut("{id:guid}")] public async Task>> Update( Guid id, [FromBody] UpdateBreweryCommand command ) { if (command.BreweryPostId != id) return BadRequest(new ResponseBody { Message = "Route ID does not match payload ID." }); BreweryDto brewery = await mediator.Send(command); return Ok( new ResponseBody { Message = "Brewery updated successfully.", Payload = brewery, } ); } /// Deletes a brewery post. /// 200 OK confirming the deletion; 404 Not Found if no brewery exists with . [HttpDelete("{id:guid}")] public async Task> Delete(Guid id) { await mediator.Send(new DeleteBreweryCommand(id)); return Ok(new ResponseBody { Message = "Brewery deleted successfully." }); } }