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." });
}
}