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;
using OmsorgCore.Engine.Events;

namespace OmsorgCore.Api.Controllers;

[ApiController]
[Authorize]
[Route("api/employees")]
public class EmployeesController : ControllerBase
{
    private const string StatusListKey = "EmployeeStatus";
    private const string EmploymentTypeListKey = "EmploymentType";
    private const string QualificationListKey = "Qualification";

    private readonly IEmployeeService _employeeService;
    private readonly IValueListRepository _valueListRepository;
    private readonly IDomainEventDispatcher _dispatcher;

    public EmployeesController(IEmployeeService employeeService, IValueListRepository valueListRepository, IDomainEventDispatcher dispatcher)
    {
        _employeeService = employeeService;
        _valueListRepository = valueListRepository;
        _dispatcher = dispatcher;
    }

    [HttpGet]
    [RequirePermission(ModuleType.Employees, PermissionAction.View)]
    public async Task<ActionResult<PagedResponse<EmployeeResponse>>> GetAll(
        [FromQuery] string? search,
        [FromQuery] string? status,
        [FromQuery] string? employmentType,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20,
        CancellationToken cancellationToken = default)
    {
        if (employmentType is not null)
        {
            var allowedTypes = await _valueListRepository.GetActiveValuesAsync(EmploymentTypeListKey, cancellationToken);
            if (!allowedTypes.Contains(employmentType))
            {
                return BadRequest($"employmentType muss einer der folgenden Werte sein: {string.Join(", ", allowedTypes)}.");
            }
        }

        page = Math.Max(page, 1);
        pageSize = Math.Clamp(pageSize, 1, 100);

        var (items, totalCount) = await _employeeService.GetPagedAsync(search, status, employmentType, page, pageSize, cancellationToken);
        return Ok(new PagedResponse<EmployeeResponse>(items.Select(ToResponse).ToList(), totalCount, page, pageSize));
    }

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

    [HttpPost]
    [RequirePermission(ModuleType.Employees, PermissionAction.Create)]
    public async Task<ActionResult<EmployeeResponse>> Create(CreateEmployeeRequest request, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(request.FirstName) || request.FirstName.Length > 200)
        {
            return BadRequest("FirstName ist erforderlich und darf maximal 200 Zeichen lang sein.");
        }

        if (string.IsNullOrWhiteSpace(request.LastName) || request.LastName.Length > 200)
        {
            return BadRequest("LastName ist erforderlich und darf maximal 200 Zeichen lang sein.");
        }

        if (request.PhoneNumber is { Length: > 50 })
        {
            return BadRequest("PhoneNumber darf maximal 50 Zeichen lang sein.");
        }

        if (request.Email is { Length: > 200 })
        {
            return BadRequest("Email darf maximal 200 Zeichen lang sein.");
        }

        if (request.EntryDate is not null && request.ExitDate is not null && request.ExitDate < request.EntryDate)
        {
            return BadRequest("ExitDate darf nicht vor EntryDate liegen.");
        }

        var addressError = ValidateAddressFields(request.Street, request.PostalCode, request.City, request.Country);
        if (addressError is not null)
        {
            return BadRequest(addressError);
        }

        var stammdatenError = await ValidateStammdatenFieldsAsync(
            request.EmergencyContactName,
            request.EmergencyContactPhone,
            request.EmergencyContactRelation,
            request.EmploymentType,
            request.Qualification,
            cancellationToken);
        if (stammdatenError is not null)
        {
            return BadRequest(stammdatenError);
        }

        var employee = new Employee
        {
            FirstName = request.FirstName,
            LastName = request.LastName,
            DateOfBirth = request.DateOfBirth,
            Street = request.Street,
            PostalCode = request.PostalCode,
            City = request.City,
            Country = request.Country,
            PhoneNumber = request.PhoneNumber,
            Email = request.Email,
            EntryDate = request.EntryDate,
            ExitDate = request.ExitDate,
            EmergencyContactName = request.EmergencyContactName,
            EmergencyContactPhone = request.EmergencyContactPhone,
            EmergencyContactRelation = request.EmergencyContactRelation,
            EmploymentType = request.EmploymentType,
            Qualification = request.Qualification
        };

        var created = await _employeeService.CreateAsync(employee, cancellationToken);
        await _dispatcher.DispatchAsync(new EmployeeCreatedEvent(created.Id), cancellationToken);

        return CreatedAtAction(nameof(GetById), new { id = created.Id }, ToResponse(created));
    }

    [HttpPut("{id:guid}")]
    [RequirePermission(ModuleType.Employees, PermissionAction.Edit)]
    public async Task<ActionResult<EmployeeResponse>> Update(Guid id, UpdateEmployeeRequest request, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(request.FirstName) || request.FirstName.Length > 200)
        {
            return BadRequest("FirstName ist erforderlich und darf maximal 200 Zeichen lang sein.");
        }

        if (string.IsNullOrWhiteSpace(request.LastName) || request.LastName.Length > 200)
        {
            return BadRequest("LastName ist erforderlich und darf maximal 200 Zeichen lang sein.");
        }

        if (string.IsNullOrWhiteSpace(request.Status) || request.Status.Length > 50)
        {
            return BadRequest("Status ist erforderlich und darf maximal 50 Zeichen lang sein.");
        }

        var allowedStatuses = await _valueListRepository.GetActiveValuesAsync(StatusListKey, cancellationToken);
        if (!allowedStatuses.Contains(request.Status))
        {
            return BadRequest($"Status muss einer der folgenden Werte sein: {string.Join(", ", allowedStatuses)}.");
        }

        if (request.PhoneNumber is { Length: > 50 })
        {
            return BadRequest("PhoneNumber darf maximal 50 Zeichen lang sein.");
        }

        if (request.Email is { Length: > 200 })
        {
            return BadRequest("Email darf maximal 200 Zeichen lang sein.");
        }

        if (request.EntryDate is not null && request.ExitDate is not null && request.ExitDate < request.EntryDate)
        {
            return BadRequest("ExitDate darf nicht vor EntryDate liegen.");
        }

        var addressError = ValidateAddressFields(request.Street, request.PostalCode, request.City, request.Country);
        if (addressError is not null)
        {
            return BadRequest(addressError);
        }

        var stammdatenError = await ValidateStammdatenFieldsAsync(
            request.EmergencyContactName,
            request.EmergencyContactPhone,
            request.EmergencyContactRelation,
            request.EmploymentType,
            request.Qualification,
            cancellationToken);
        if (stammdatenError is not null)
        {
            return BadRequest(stammdatenError);
        }

        var updates = new Employee
        {
            FirstName = request.FirstName,
            LastName = request.LastName,
            Status = request.Status,
            DateOfBirth = request.DateOfBirth,
            Street = request.Street,
            PostalCode = request.PostalCode,
            City = request.City,
            Country = request.Country,
            PhoneNumber = request.PhoneNumber,
            Email = request.Email,
            EntryDate = request.EntryDate,
            ExitDate = request.ExitDate,
            EmergencyContactName = request.EmergencyContactName,
            EmergencyContactPhone = request.EmergencyContactPhone,
            EmergencyContactRelation = request.EmergencyContactRelation,
            EmploymentType = request.EmploymentType,
            Qualification = request.Qualification,
            AvatarFileName = request.AvatarFileName
        };

        var updated = await _employeeService.UpdateAsync(id, updates, cancellationToken);
        return updated is null ? NotFound() : Ok(ToResponse(updated));
    }

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

    private static string? ValidateAddressFields(string? street, string? postalCode, string? city, string? country)
    {
        if (street is { Length: > 200 })
        {
            return "Street darf maximal 200 Zeichen lang sein.";
        }

        if (postalCode is { Length: > 10 })
        {
            return "PostalCode darf maximal 10 Zeichen lang sein.";
        }

        if (city is { Length: > 100 })
        {
            return "City darf maximal 100 Zeichen lang sein.";
        }

        if (country is { Length: > 100 })
        {
            return "Country darf maximal 100 Zeichen lang sein.";
        }

        return null;
    }

    private async Task<string?> ValidateStammdatenFieldsAsync(
        string? emergencyContactName,
        string? emergencyContactPhone,
        string? emergencyContactRelation,
        string? employmentType,
        string? qualification,
        CancellationToken cancellationToken)
    {
        if (emergencyContactName is { Length: > 200 })
        {
            return "EmergencyContactName darf maximal 200 Zeichen lang sein.";
        }

        if (emergencyContactPhone is { Length: > 50 })
        {
            return "EmergencyContactPhone darf maximal 50 Zeichen lang sein.";
        }

        if (emergencyContactRelation is { Length: > 100 })
        {
            return "EmergencyContactRelation darf maximal 100 Zeichen lang sein.";
        }

        if (employmentType is not null)
        {
            var allowedTypes = await _valueListRepository.GetActiveValuesAsync(EmploymentTypeListKey, cancellationToken);
            if (!allowedTypes.Contains(employmentType))
            {
                return $"EmploymentType muss einer der folgenden Werte sein: {string.Join(", ", allowedTypes)}.";
            }
        }

        if (qualification is not null)
        {
            var allowedQualifications = await _valueListRepository.GetActiveValuesAsync(QualificationListKey, cancellationToken);
            if (!allowedQualifications.Contains(qualification))
            {
                return $"Qualification muss einer der folgenden Werte sein: {string.Join(", ", allowedQualifications)}.";
            }
        }

        return null;
    }

    private static EmployeeResponse ToResponse(Employee employee)
        => new(
            employee.Id,
            employee.FirstName,
            employee.LastName,
            employee.Status,
            employee.DateOfBirth,
            employee.Street,
            employee.PostalCode,
            employee.City,
            employee.Country,
            employee.PhoneNumber,
            employee.Email,
            employee.EntryDate,
            employee.ExitDate,
            employee.EmergencyContactName,
            employee.EmergencyContactPhone,
            employee.EmergencyContactRelation,
            employee.EmploymentType,
            employee.Qualification,
            employee.AvatarFileName);
}
