using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Meezi.API.Models.Shifts;
using Meezi.API.Services;
using Meezi.Core.Interfaces;
using Meezi.Shared;

namespace Meezi.API.Controllers;

[Route("api/cafes/{cafeId}/branches/{branchId}/shifts")]
public class ShiftsController : CafeApiControllerBase
{
    private readonly IShiftService _shifts;
    private readonly IValidator<OpenShiftRequest> _openValidator;
    private readonly IValidator<CloseShiftRequest> _closeValidator;

    public ShiftsController(
        IShiftService shifts,
        IValidator<OpenShiftRequest> openValidator,
        IValidator<CloseShiftRequest> closeValidator)
    {
        _shifts = shifts;
        _openValidator = openValidator;
        _closeValidator = closeValidator;
    }

    [HttpPost("open")]
    public async Task<IActionResult> Open(
        string cafeId,
        string branchId,
        [FromBody] OpenShiftRequest request,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        if (string.IsNullOrEmpty(tenant.UserId))
            return StatusCode(StatusCodes.Status401Unauthorized,
                new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));

        var validation = await _openValidator.ValidateAsync(request, ct);
        if (!validation.IsValid) return BadRequest(ValidationError(validation));

        var result = await _shifts.OpenShiftAsync(cafeId, branchId, request.OpeningCash, tenant.UserId, ct);
        return ShiftResult(result);
    }

    [HttpPost("{id}/close")]
    public async Task<IActionResult> Close(
        string cafeId,
        string branchId,
        string id,
        [FromBody] CloseShiftRequest request,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        if (string.IsNullOrEmpty(tenant.UserId))
            return StatusCode(StatusCodes.Status401Unauthorized,
                new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));

        var validation = await _closeValidator.ValidateAsync(request, ct);
        if (!validation.IsValid) return BadRequest(ValidationError(validation));

        var result = await _shifts.CloseShiftAsync(cafeId, id, request.ClosingCash, tenant.UserId, ct);
        return ShiftResult(result);
    }

    [HttpGet("current")]
    public async Task<IActionResult> Current(
        string cafeId,
        string branchId,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;

        var data = await _shifts.GetCurrentShiftAsync(cafeId, branchId, ct);
        if (data is null)
            return NotFound(new ApiResponse<object>(false, null,
                new ApiError("NO_OPEN_SHIFT", "No open cash register shift for this branch.")));

        return Ok(new ApiResponse<ShiftDto>(true, data));
    }

    [HttpGet("{id}/transactions")]
    public async Task<IActionResult> Transactions(
        string cafeId,
        string branchId,
        string id,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;

        var data = await _shifts.GetTransactionsAsync(cafeId, id, ct);
        if (data is null) return NotFoundError();
        return Ok(new ApiResponse<IReadOnlyList<CashTransactionDto>>(true, data));
    }

    private IActionResult ShiftResult(ShiftServiceResult<ShiftDto> result) =>
        result.ErrorCode switch
        {
            null => Ok(new ApiResponse<ShiftDto>(true, result.Data)),
            "SHIFT_ALREADY_OPEN" => Conflict(new ApiResponse<object>(false, null,
                new ApiError(result.ErrorCode, "This branch already has an open shift.", result.Field))),
            "BRANCH_NOT_FOUND" => NotFound(new ApiResponse<object>(false, null,
                new ApiError(result.ErrorCode, "Branch not found.", result.Field))),
            "SHIFT_NOT_FOUND" => NotFound(new ApiResponse<object>(false, null,
                new ApiError(result.ErrorCode, "Shift not found.", result.Field))),
            "SHIFT_ALREADY_CLOSED" => BadRequest(new ApiResponse<object>(false, null,
                new ApiError(result.ErrorCode, "Shift is already closed.", result.Field))),
            _ => BadRequest(new ApiResponse<object>(false, null,
                new ApiError(result.ErrorCode ?? "SHIFT_ERROR", "Shift operation failed.", result.Field)))
        };
}
