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

namespace OmsorgCore.Api.Controllers;

/// <summary>
/// Selbstbedienungs-Endpoint für Außendienst-Mitarbeiter (OMSORG Connect,
/// `omsorgWeb/mitarbeiter-app`): eigene Fahrtstrecke (km) je Einrichtung pflegen, Grundlage für die
/// kilometerbasierte Fahrtkostenabrechnung (Facility.TravelCostMode == "ProKilometer", FR-EIN-4).
/// Bewusst ein eigener ModuleType (EmployeeFacilityDistances) statt Facilities-Rechten — der
/// Außendienst darf hier ausschließlich seine eigenen Datensätze sehen/anlegen/bearbeiten, nicht die
/// Konditionen/CRM-Daten einer Einrichtung. EmployeeId kommt IMMER serverseitig aus dem JWT-Claim,
/// nie vom Client (analog AbsencesController/TimeEntriesController).
/// </summary>
[ApiController]
[Authorize]
[Route("api/me/facility-distances")]
public class MyFacilityDistancesController : ControllerBase
{
    private readonly IEmployeeFacilityDistanceService _employeeFacilityDistanceService;
    private readonly IFacilityService _facilityService;
    private readonly ICurrentUserService _currentUserService;

    public MyFacilityDistancesController(
        IEmployeeFacilityDistanceService employeeFacilityDistanceService,
        IFacilityService facilityService,
        ICurrentUserService currentUserService)
    {
        _employeeFacilityDistanceService = employeeFacilityDistanceService;
        _facilityService = facilityService;
        _currentUserService = currentUserService;
    }

    [HttpGet]
    [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.View)]
    public async Task<ActionResult<IReadOnlyList<MyFacilityDistanceResponse>>> GetAll(CancellationToken cancellationToken)
    {
        var employeeId = RequireOwnEmployeeId();
        if (employeeId is null)
        {
            return BadRequest("Dieser Benutzer ist mit keinem Mitarbeiter verknüpft.");
        }

        var facilities = (await _facilityService.GetAllAsync(cancellationToken)).ToDictionary(f => f.Id);
        var all = new List<MyFacilityDistanceResponse>();

        foreach (var facility in facilities.Values)
        {
            var distance = await _employeeFacilityDistanceService.GetByFacilityAndEmployeeAsync(facility.Id, employeeId.Value, cancellationToken);
            if (distance is not null)
            {
                all.Add(new MyFacilityDistanceResponse(distance.Id, facility.Id, facility.Name, distance.DistanceKm));
            }
        }

        return Ok(all);
    }

    [HttpGet("facilities")]
    [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.View)]
    public async Task<ActionResult<IReadOnlyList<FacilityOptionResponse>>> GetFacilityOptions(CancellationToken cancellationToken)
    {
        var facilities = await _facilityService.GetAllAsync(cancellationToken);
        return Ok(facilities.OrderBy(f => f.Name).Select(f => new FacilityOptionResponse(f.Id, f.Name)).ToList());
    }

    [HttpPost]
    [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.Create)]
    public async Task<ActionResult<MyFacilityDistanceResponse>> Create(CreateMyFacilityDistanceRequest request, CancellationToken cancellationToken)
    {
        var employeeId = RequireOwnEmployeeId();
        if (employeeId is null)
        {
            return BadRequest("Dieser Benutzer ist mit keinem Mitarbeiter verknüpft.");
        }

        var facility = await _facilityService.GetByIdAsync(request.FacilityId, cancellationToken);
        if (facility is null)
        {
            return BadRequest("FacilityId verweist auf keine existierende Einrichtung.");
        }

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

        if (await _employeeFacilityDistanceService.GetByFacilityAndEmployeeAsync(request.FacilityId, employeeId.Value, cancellationToken) is not null)
        {
            return BadRequest("Für diese Einrichtung existiert bereits eine eigene Entfernung.");
        }

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

        var created = await _employeeFacilityDistanceService.CreateAsync(distance, cancellationToken);
        return CreatedAtAction(nameof(GetAll), new MyFacilityDistanceResponse(created.Id, facility.Id, facility.Name, created.DistanceKm));
    }

    [HttpPut("{id:guid}")]
    [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.Edit)]
    public async Task<ActionResult<MyFacilityDistanceResponse>> Update(Guid id, UpdateMyFacilityDistanceRequest request, CancellationToken cancellationToken)
    {
        var employeeId = RequireOwnEmployeeId();
        if (employeeId is null)
        {
            return BadRequest("Dieser Benutzer ist mit keinem Mitarbeiter verknüpft.");
        }

        var existing = await _employeeFacilityDistanceService.GetByIdAsync(id, cancellationToken);
        if (existing is null || existing.EmployeeId != employeeId.Value)
        {
            return NotFound();
        }

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

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

        var facility = await _facilityService.GetByIdAsync(updated.FacilityId, cancellationToken);
        return Ok(new MyFacilityDistanceResponse(updated.Id, updated.FacilityId, facility?.Name ?? "", updated.DistanceKm));
    }

    private Guid? RequireOwnEmployeeId()
        => _currentUserService.EmployeeId is { } id && id != Guid.Empty ? id : null;
}
