using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Baya.Application.Features.Bookings.Commands.CancelBooking;
using Baya.Application.Features.Bookings.Commands.ConvertRequestToBooking;
using Baya.Application.Features.Bookings.Commands.SubmitCareInstructions;
using Baya.Application.Features.Bookings.Commands.TransitionBookingStatus;
using Baya.Application.Features.Bookings.Queries.GetBookingDetail;
using Baya.Application.Features.Bookings.Queries.GetCareInstructions;
using Baya.Application.Features.Bookings.Queries.ListBookings;
using Baya.Application.Features.Refunds.Commands.CancelBookingAndRefund;
using Baya.Application.Features.Refunds.Queries.GetCancellationPolicyPreview;
using Baya.Application.Models.Booking;
using Baya.Application.Models.Common;
using Baya.Application.Models.Refunds;
using Baya.WebFramework.Attributes;
using Baya.WebFramework.BaseController;
using Baya.WebFramework.ServiceConfiguration;
using Mediator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace Baya.Web.Api.Controllers.V1;

/// <summary>
/// The confirmed-booking surface: the (mock) payment-capture conversion, role-scoped detail + list, the
/// admin/dispute status transition, and the stage-2 care-instructions submit (customer/admin) + gated read
/// (assigned nurse/admin, post-confirmation). Tenancy is enforced in the handlers; money is IRR digit-strings.
/// </summary>
[ApiVersion("1")]
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[Authorize]
[Display(Description = "Confirmed bookings (convert/detail/list, transition, care instructions)")]
public sealed class BookingsController(ISender sender) : BaseController
{
    // The mock/test conversion path — b10's real card capture calls ConvertRequestToBooking directly.
    [HttpPost("[action]")]
    [EnableRateLimiting(RateLimitingServiceExtension.SensitivePolicy)]
    [ProducesOkApiResponseType<BookingDetailDto>]
    public async Task<IActionResult> Convert(ConvertRequestToBookingCommand command, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(command, cancellationToken));

    [HttpGet("[action]/{id}")]
    [ProducesOkApiResponseType<BookingDetailDto>]
    public async Task<IActionResult> Get(long id, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(new GetBookingDetailQuery(id), cancellationToken));

    [HttpGet("[action]")]
    [ProducesOkApiResponseType<PagedResult<BookingListItemDto>>]
    public async Task<IActionResult> List([FromQuery] ListBookingsQuery query, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(query, cancellationToken));

    // Admin/dispute moves only (enforced in the handler); cancellation goes through Cancel below.
    [HttpPost("[action]/{id}")]
    [ProducesOkApiResponseType<BookingDetailDto>]
    public async Task<IActionResult> Transition(long id, TransitionBookingStatusCommand command, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(command with { BookingId = id }, cancellationToken));

    [HttpPost("[action]/{id}")]
    [EnableRateLimiting(RateLimitingServiceExtension.SensitivePolicy)]
    [ProducesOkApiResponseType<CancellationResultDto>]
    public async Task<IActionResult> Cancel(long id, CancelBookingCommand command, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(command with { BookingId = id }, cancellationToken));

    // Customer-initiated cancel: cancels the booking AND opens its refund in one call (REQ-019).
    [HttpPost("{id}/cancel")]
    [EnableRateLimiting(RateLimitingServiceExtension.SensitivePolicy)]
    [ProducesOkApiResponseType<RefundStatusDto>]
    public async Task<IActionResult> CancelAndRefund(long id, CancelBookingAndRefundCommand command, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(command with { BookingId = id }, cancellationToken));

    // Pre-cancel disclosure: the applicable policy + per-session refundability, resolved by current lead time (REQ-020).
    [HttpGet("{id}/cancellation_policy")]
    [ProducesOkApiResponseType<CancellationPolicyPreviewDto>]
    public async Task<IActionResult> CancellationPolicy(long id, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(new GetCancellationPolicyPreviewQuery(id), cancellationToken));

    [HttpPost("[action]/{id}")]
    [ProducesOkApiResponseType<CareInstructionsDto>]
    public async Task<IActionResult> SubmitCareInstructions(long id, SubmitCareInstructionsCommand command, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(command with { BookingId = id }, cancellationToken));

    // Gated stage-2 read — assigned nurse + admin only, post-confirmation.
    [HttpGet("[action]/{id}")]
    [ProducesOkApiResponseType<CareInstructionsDto>]
    public async Task<IActionResult> CareInstructions(long id, CancellationToken cancellationToken)
        => OperationResult(await sender.Send(new GetCareInstructionsQuery(id), cancellationToken));
}
