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;

namespace OmsorgCore.Api.Controllers;

[ApiController]
[Authorize]
[Route("api/assignments")]
public class AssignmentsController : ControllerBase
{
    private readonly IAssignmentService _assignmentService;

    public AssignmentsController(IAssignmentService assignmentService)
    {
        _assignmentService = assignmentService;
    }

    [HttpGet]
    [RequirePermission(ModuleType.Assignments, PermissionAction.View)]
    public async Task<ActionResult<PagedResponse<AssignmentResponse>>> GetAll(
        [FromQuery] Guid? orderId = null,
        [FromQuery] Guid? employeeId = null,
        [FromQuery] DateOnly? fromDate = null,
        [FromQuery] DateOnly? toDate = null,
        [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 _assignmentService.GetPagedAsync(
            orderId, employeeId, fromDate, toDate, page, pageSize, cancellationToken);
        var responses = items.Select(a => ToResponse(a)).ToList();
        return Ok(new PagedResponse<AssignmentResponse>(responses, totalCount, page, pageSize));
    }

    /// <summary>
    /// Läuft dieselben FR-EM-3-Konfliktprüfungen wie <see cref="Create"/>, ohne etwas anzulegen -
    /// für die Live-Vorschau im Zuweisungs-Dialog, sobald Mitarbeiter/Zeitraum ausgewählt sind.
    /// </summary>
    [HttpGet("check")]
    [RequirePermission(ModuleType.Assignments, PermissionAction.Create)]
    public async Task<ActionResult<IReadOnlyList<AssignmentConflictResponse>>> Check(
        [FromQuery] Guid orderId,
        [FromQuery] Guid employeeId,
        [FromQuery] DateOnly startDate,
        [FromQuery] DateOnly endDate,
        [FromQuery] Guid? excludeAssignmentId,
        CancellationToken cancellationToken)
    {
        var conflicts = await _assignmentService.CheckConflictsAsync(
            orderId, employeeId, startDate, endDate, excludeAssignmentId, cancellationToken);
        return Ok(conflicts.Select(ToConflictResponse).ToList());
    }

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

    [HttpPost]
    [RequirePermission(ModuleType.Assignments, PermissionAction.Create)]
    public async Task<ActionResult<AssignmentResponse>> Create(
        CreateAssignmentRequest request,
        CancellationToken cancellationToken)
    {
        var result = await _assignmentService.CreateAsync(
            request.OrderId,
            request.EmployeeId,
            request.StartDate,
            request.EndDate,
            request.Note,
            cancellationToken);

        if (!result.Success)
        {
            return result.Reason switch
            {
                CreateAssignmentFailureReason.OrderNotFound => NotFound(new { error = "order_not_found" }),
                CreateAssignmentFailureReason.EmployeeNotFound => NotFound(new { error = "employee_not_found" }),
                CreateAssignmentFailureReason.InvalidDateRange => BadRequest(new { error = "invalid_date_range", message = "End date must be >= start date" }),
                CreateAssignmentFailureReason.DateOutsideOrderRange => BadRequest(new { error = "date_outside_order_range", message = "Assignment dates must be within the order's date range" }),
                CreateAssignmentFailureReason.OverlappingAssignment => Conflict(new { error = "overlapping_assignment", message = "Employee already has an assignment in this time range for this order" }),
                CreateAssignmentFailureReason.ValidationConflict => Conflict(new { error = "validation_conflict", conflicts = result.Conflicts.Select(ToConflictResponse) }),
                _ => BadRequest(new { error = "unknown_error" })
            };
        }

        return CreatedAtAction(nameof(GetById), new { id = result.Assignment!.Id }, ToResponse(result.Assignment, result.Conflicts));
    }

    [HttpPut("{id:guid}")]
    [RequirePermission(ModuleType.Assignments, PermissionAction.Edit)]
    public async Task<ActionResult<AssignmentResponse>> Update(
        Guid id,
        UpdateAssignmentRequest request,
        CancellationToken cancellationToken)
    {
        var assignment = await _assignmentService.UpdateAsync(id, request.Note, cancellationToken);
        return assignment is null ? NotFound() : Ok(ToResponse(assignment));
    }

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

    private static AssignmentResponse ToResponse(Assignment assignment, IReadOnlyList<AssignmentConflict>? conflicts = null)
        => new(
            assignment.Id,
            assignment.OrderId,
            assignment.EmployeeId,
            assignment.Employee.FirstName,
            assignment.Employee.LastName,
            assignment.StartDate,
            assignment.EndDate,
            assignment.Note,
            (conflicts ?? Array.Empty<AssignmentConflict>()).Select(ToConflictResponse).ToList());

    private static AssignmentConflictResponse ToConflictResponse(AssignmentConflict conflict)
        => new(conflict.Type.ToString(), conflict.Severity.ToString(), conflict.Message);
}
