using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OmsorgCore.Api.Contracts;
using OmsorgCore.Api.Security;
using OmsorgCore.Application.Services;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Domain.Enums;
using OmsorgCore.Engine.Events;

namespace OmsorgCore.Api.Controllers;

[ApiController]
[Authorize]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
    private const string StatusListKey = "OrderStatus";
    private const string QualificationListKey = "Qualification";
    private const string ShiftTypeListKey = "ShiftType";
    private const string PriorityListKey = "Priority";

    private readonly IOrderService _orderService;
    private readonly IFacilityContactService _facilityContactService;
    private readonly IValueListService _valueListService;
    private readonly IDomainEventDispatcher _dispatcher;

    public OrdersController(
        IOrderService orderService,
        IFacilityContactService facilityContactService,
        IValueListService valueListService,
        IDomainEventDispatcher dispatcher)
    {
        _orderService = orderService;
        _facilityContactService = facilityContactService;
        _valueListService = valueListService;
        _dispatcher = dispatcher;
    }

    [HttpGet]
    [RequirePermission(ModuleType.Orders, PermissionAction.View)]
    public async Task<ActionResult<PagedResponse<OrderResponse>>> GetAll(
        [FromQuery] string? search,
        [FromQuery] Guid? statusId,
        [FromQuery] Guid? facilityId,
        [FromQuery] string? priority,
        [FromQuery] string? requiredQualification,
        [FromQuery] string? shiftType,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20,
        CancellationToken cancellationToken = default)
    {
        page = Math.Max(page, 1);
        pageSize = Math.Clamp(pageSize, 1, 100);

        var (items, totalCount) = await _orderService.GetPagedAsync(
            search, statusId, facilityId, priority, requiredQualification, shiftType, page, pageSize, cancellationToken);
        return Ok(new PagedResponse<OrderResponse>(items.Select(ToResponse).ToList(), totalCount, page, pageSize));
    }

    [HttpGet("{id:guid}")]
    [RequirePermission(ModuleType.Orders, PermissionAction.View)]
    public async Task<ActionResult<OrderResponse>> GetById(Guid id, CancellationToken cancellationToken)
    {
        var order = await _orderService.GetByIdAsync(id, cancellationToken);
        return order is null ? NotFound() : Ok(ToResponse(order));
    }

    [HttpPost]
    [RequirePermission(ModuleType.Orders, PermissionAction.Create)]
    public async Task<ActionResult<OrderResponse>> Create(CreateOrderRequest request, CancellationToken cancellationToken)
    {
        var fieldError = await ValidateFieldsAsync(
            request.FacilityId,
            request.StartDate,
            request.EndDate,
            request.RequiredQualification,
            request.ShiftType,
            request.RequiredHeadcount,
            request.Conditions,
            request.Priority,
            cancellationToken);
        if (fieldError is not null)
        {
            return BadRequest(fieldError);
        }

        var contactError = await ValidateFacilityContactAsync(request.FacilityContactId, request.FacilityId, cancellationToken);
        if (contactError is not null)
        {
            return BadRequest(contactError);
        }

        var order = new Order
        {
            FacilityId = request.FacilityId,
            FacilityContactId = request.FacilityContactId,
            StartDate = request.StartDate,
            EndDate = request.EndDate,
            RequiredQualification = request.RequiredQualification,
            ShiftType = request.ShiftType,
            RequiredHeadcount = request.RequiredHeadcount,
            Conditions = request.Conditions,
            Priority = request.Priority
        };

        var created = await _orderService.CreateAsync(order, cancellationToken);
        await _dispatcher.DispatchAsync(new OrderCreatedEvent(created.Id), cancellationToken);

        var reloaded = await _orderService.GetByIdAsync(created.Id, cancellationToken) ?? created;
        return CreatedAtAction(nameof(GetById), new { id = created.Id }, ToResponse(reloaded));
    }

    [HttpPut("{id:guid}")]
    [RequirePermission(ModuleType.Orders, PermissionAction.Edit)]
    public async Task<ActionResult<OrderResponse>> Update(Guid id, UpdateOrderRequest request, CancellationToken cancellationToken)
    {
        var fieldError = await ValidateFieldsAsync(
            request.FacilityId,
            request.StartDate,
            request.EndDate,
            request.RequiredQualification,
            request.ShiftType,
            request.RequiredHeadcount,
            request.Conditions,
            request.Priority,
            cancellationToken);
        if (fieldError is not null)
        {
            return BadRequest(fieldError);
        }

        var contactError = await ValidateFacilityContactAsync(request.FacilityContactId, request.FacilityId, cancellationToken);
        if (contactError is not null)
        {
            return BadRequest(contactError);
        }

        var statusItems = await _valueListService.GetItemsAsync(StatusListKey, cancellationToken);
        if (!statusItems.Any(s => s.Id == request.StatusId))
        {
            return BadRequest("StatusId verweist auf keinen bekannten Auftragsstatus.");
        }

        var updates = new Order
        {
            FacilityId = request.FacilityId,
            FacilityContactId = request.FacilityContactId,
            StartDate = request.StartDate,
            EndDate = request.EndDate,
            RequiredQualification = request.RequiredQualification,
            ShiftType = request.ShiftType,
            RequiredHeadcount = request.RequiredHeadcount,
            Conditions = request.Conditions,
            Priority = request.Priority,
            StatusId = request.StatusId
        };

        var result = await _orderService.UpdateAsync(id, updates, cancellationToken);
        if (!result.Success)
        {
            return result.FailureReason switch
            {
                UpdateOrderFailureReason.OrderNotFound => NotFound(),
                UpdateOrderFailureReason.InvalidStatusTransition => BadRequest("Der Statuswechsel ist nicht zulässig."),
                _ => BadRequest()
            };
        }

        return Ok(ToResponse(result.Order!));
    }

    [HttpDelete("{id:guid}")]
    [RequirePermission(ModuleType.Orders, PermissionAction.Delete)]
    public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
    {
        var deleted = await _orderService.DeleteAsync(id, cancellationToken);
        return deleted ? NoContent() : NotFound();
    }

    private async Task<string?> ValidateFacilityContactAsync(Guid? facilityContactId, Guid facilityId, CancellationToken cancellationToken)
    {
        if (facilityContactId is null)
        {
            return null;
        }

        var contact = await _facilityContactService.GetByIdAsync(facilityContactId.Value, cancellationToken);
        if (contact is null || contact.FacilityId != facilityId)
        {
            return "Der Ansprechpartner gehört nicht zur angegebenen Einrichtung.";
        }

        return null;
    }

    private async Task<string?> ValidateFieldsAsync(
        Guid facilityId,
        DateOnly startDate,
        DateOnly? endDate,
        string? requiredQualification,
        string? shiftType,
        int requiredHeadcount,
        string? conditions,
        string priority,
        CancellationToken cancellationToken)
    {
        if (facilityId == Guid.Empty)
        {
            return "FacilityId ist erforderlich.";
        }

        if (startDate == default)
        {
            return "StartDate ist erforderlich.";
        }

        if (endDate.HasValue && endDate.Value < startDate)
        {
            return "EndDate darf nicht vor StartDate liegen.";
        }

        if (requiredHeadcount < 1)
        {
            return "RequiredHeadcount muss mindestens 1 sein.";
        }

        if (string.IsNullOrWhiteSpace(priority))
        {
            return "Priority ist erforderlich.";
        }

        var priorityItems = await _valueListService.GetItemsAsync(PriorityListKey, cancellationToken);
        if (!priorityItems.Any(i => i.Value == priority))
        {
            return $"Priority muss einer der folgenden Werte sein: {string.Join(", ", priorityItems.Select(i => i.Value))}.";
        }

        if (requiredQualification is not null)
        {
            var qualificationItems = await _valueListService.GetItemsAsync(QualificationListKey, cancellationToken);
            if (!qualificationItems.Any(i => i.Value == requiredQualification))
            {
                return $"RequiredQualification muss einer der folgenden Werte sein: {string.Join(", ", qualificationItems.Select(i => i.Value))}.";
            }
        }

        if (shiftType is not null)
        {
            var shiftTypeItems = await _valueListService.GetItemsAsync(ShiftTypeListKey, cancellationToken);
            if (!shiftTypeItems.Any(i => i.Value == shiftType))
            {
                return $"ShiftType muss einer der folgenden Werte sein: {string.Join(", ", shiftTypeItems.Select(i => i.Value))}.";
            }
        }

        if (conditions is { Length: > 500 })
        {
            return "Conditions darf maximal 500 Zeichen lang sein.";
        }

        return null;
    }

    private static OrderResponse ToResponse(Order order)
        => new(
            order.Id,
            order.FacilityId,
            order.FacilityContactId,
            order.StartDate,
            order.EndDate,
            order.RequiredQualification,
            order.ShiftType,
            order.RequiredHeadcount,
            order.Conditions,
            order.Priority,
            order.StatusId,
            order.Status?.Value ?? string.Empty);
}
