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;

/// <summary>
/// Mitarbeiter-Einrichtung-Entfernungen (km) sind eine 1:n-Unterressource von Facility (FR-EIN-4,
/// Grundlage für Facility.TravelCostMode == "ProKilometer") — kein eigenständiges Core-Objekt, daher
/// unter /api/facilities/{facilityId}/employee-distances und mit den gleichen Facilities-Rechten
/// gegated statt einem eigenen ModuleType, analog zu FacilityQualificationRate.
/// </summary>
[ApiController]
[Authorize]
[Route("api/facilities/{facilityId:guid}/employee-distances")]
public class EmployeeFacilityDistancesController : ControllerBase
{
    private readonly IFacilityService _facilityService;
    private readonly IEmployeeService _employeeService;
    private readonly IEmployeeFacilityDistanceService _employeeFacilityDistanceService;

    public EmployeeFacilityDistancesController(
        IFacilityService facilityService,
        IEmployeeService employeeService,
        IEmployeeFacilityDistanceService employeeFacilityDistanceService)
    {
        _facilityService = facilityService;
        _employeeService = employeeService;
        _employeeFacilityDistanceService = employeeFacilityDistanceService;
    }

    [HttpGet]
    [RequirePermission(ModuleType.Facilities, PermissionAction.View)]
    public async Task<ActionResult<IReadOnlyList<EmployeeFacilityDistanceResponse>>> GetAll(Guid facilityId, CancellationToken cancellationToken)
    {
        if (await _facilityService.GetByIdAsync(facilityId, cancellationToken) is null)
        {
            return NotFound();
        }

        var distances = await _employeeFacilityDistanceService.GetByFacilityIdAsync(facilityId, cancellationToken);
        return Ok(distances.Select(d => ToResponse(d, d.Employee)).ToList());
    }

    [HttpPost]
    [RequirePermission(ModuleType.Facilities, PermissionAction.Create)]
    public async Task<ActionResult<EmployeeFacilityDistanceResponse>> Create(Guid facilityId, CreateEmployeeFacilityDistanceRequest request, CancellationToken cancellationToken)
    {
        if (await _facilityService.GetByIdAsync(facilityId, cancellationToken) is null)
        {
            return NotFound();
        }

        var employee = await _employeeService.GetByIdAsync(request.EmployeeId, cancellationToken);
        if (employee is null)
        {
            return BadRequest("EmployeeId verweist auf keinen existierenden Mitarbeiter.");
        }

        if (request.DistanceKm < 0)
        {
            return BadRequest("DistanceKm darf nicht negativ sein.");
        }

        if (await _employeeFacilityDistanceService.GetByFacilityAndEmployeeAsync(facilityId, request.EmployeeId, cancellationToken) is not null)
        {
            return BadRequest("Für diesen Mitarbeiter existiert bereits eine Entfernung zu dieser Einrichtung.");
        }

        var distance = new EmployeeFacilityDistance
        {
            FacilityId = facilityId,
            EmployeeId = request.EmployeeId,
            DistanceKm = request.DistanceKm
        };

        var created = await _employeeFacilityDistanceService.CreateAsync(distance, cancellationToken);
        return CreatedAtAction(nameof(GetAll), new { facilityId }, ToResponse(created, employee));
    }

    [HttpPut("{id:guid}")]
    [RequirePermission(ModuleType.Facilities, PermissionAction.Edit)]
    public async Task<ActionResult<EmployeeFacilityDistanceResponse>> Update(Guid facilityId, Guid id, UpdateEmployeeFacilityDistanceRequest request, CancellationToken cancellationToken)
    {
        var existing = await _employeeFacilityDistanceService.GetByIdAsync(id, cancellationToken);
        if (existing is null || existing.FacilityId != facilityId)
        {
            return NotFound();
        }

        if (request.DistanceKm < 0)
        {
            return BadRequest("DistanceKm darf nicht negativ sein.");
        }

        var updates = new EmployeeFacilityDistance
        {
            DistanceKm = request.DistanceKm
        };

        var updated = await _employeeFacilityDistanceService.UpdateAsync(id, updates, cancellationToken);
        if (updated is null)
        {
            return NotFound();
        }

        var employee = await _employeeService.GetByIdAsync(updated.EmployeeId, cancellationToken);
        return Ok(ToResponse(updated, employee!));
    }

    [HttpDelete("{id:guid}")]
    [RequirePermission(ModuleType.Facilities, PermissionAction.Delete)]
    public async Task<IActionResult> Delete(Guid facilityId, Guid id, CancellationToken cancellationToken)
    {
        var existing = await _employeeFacilityDistanceService.GetByIdAsync(id, cancellationToken);
        if (existing is null || existing.FacilityId != facilityId)
        {
            return NotFound();
        }

        var deleted = await _employeeFacilityDistanceService.DeleteAsync(id, cancellationToken);
        return deleted ? NoContent() : NotFound();
    }

    private static EmployeeFacilityDistanceResponse ToResponse(EmployeeFacilityDistance distance, Employee employee)
        => new(
            distance.Id,
            distance.FacilityId,
            distance.EmployeeId,
            employee.FirstName,
            employee.LastName,
            distance.DistanceKm);
}
