using OmsorgCore.Application.Abstractions;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Domain.Enums;

namespace OmsorgCore.Application.Services;

public class EmployeeService : IEmployeeService
{
    private readonly IEmployeeRepository _employeeRepository;
    private readonly IPermissionService _permissionService;
    private readonly ICurrentUserService _currentUserService;

    public EmployeeService(
        IEmployeeRepository employeeRepository,
        IPermissionService permissionService,
        ICurrentUserService currentUserService)
    {
        _employeeRepository = employeeRepository;
        _permissionService = permissionService;
        _currentUserService = currentUserService;
    }

    public Task<IReadOnlyList<Employee>> GetAllAsync(CancellationToken cancellationToken = default)
        => _employeeRepository.GetAllAsync(cancellationToken);

    public async Task<(IReadOnlyList<Employee> Items, int TotalCount)> GetPagedAsync(
        string? search,
        string? status,
        string? employmentType,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken);
        return await _employeeRepository.GetPagedAsync(search, status, employmentType, page, pageSize, cancellationToken, restrictToEmployeeId);
    }

    public async Task<Employee?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var employee = await _employeeRepository.GetByIdAsync(id, cancellationToken);
        if (employee is null)
        {
            return null;
        }

        Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken);
        if (restrictToEmployeeId.HasValue && employee.Id != restrictToEmployeeId.Value)
        {
            return null;
        }

        return employee;
    }

    /// <summary>
    /// Own-Scope-Filterung (siehe IPermissionService.GetScopeAsync): liefert die eigene
    /// EmployeeId, wenn der aktuelle User für Employees.&lt;action&gt; nur "Own" gewährt bekommt
    /// (Guid.Empty statt null, falls kein User.EmployeeId verknüpft ist — Own ohne Anker sieht
    /// dann nichts statt versehentlich alles). Null = keine Einschränkung (Scope "All" oder kein
    /// eingeloggter User, letzteres blockiert der [RequirePermission]-Endpunkt-Gate ohnehin schon).
    /// </summary>
    private async Task<Guid?> ResolveOwnScopeRestrictionAsync(PermissionAction action, CancellationToken cancellationToken)
    {
        if (_currentUserService.UserId is not { } userId)
        {
            return null;
        }

        var scope = await _permissionService.GetScopeAsync(userId, ModuleType.Employees, action, cancellationToken);
        return scope == PermissionScope.Own ? (_currentUserService.EmployeeId ?? Guid.Empty) : null;
    }

    public async Task<Employee> CreateAsync(Employee employee, CancellationToken cancellationToken = default)
    {
        await _employeeRepository.AddAsync(employee, cancellationToken);
        await _employeeRepository.SaveChangesAsync(cancellationToken);
        return employee;
    }

    public async Task<Employee?> UpdateAsync(Guid id, Employee updates, CancellationToken cancellationToken = default)
    {
        var employee = await _employeeRepository.GetByIdAsync(id, cancellationToken);
        if (employee is null)
        {
            return null;
        }

        employee.FirstName = updates.FirstName;
        employee.LastName = updates.LastName;
        employee.DateOfBirth = updates.DateOfBirth;
        employee.Street = updates.Street;
        employee.PostalCode = updates.PostalCode;
        employee.City = updates.City;
        employee.Country = updates.Country;
        employee.PhoneNumber = updates.PhoneNumber;
        employee.Email = updates.Email;
        employee.EntryDate = updates.EntryDate;
        employee.ExitDate = updates.ExitDate;
        employee.Status = updates.Status;
        employee.EmergencyContactName = updates.EmergencyContactName;
        employee.EmergencyContactPhone = updates.EmergencyContactPhone;
        employee.EmergencyContactRelation = updates.EmergencyContactRelation;
        employee.EmploymentType = updates.EmploymentType;
        employee.Qualification = updates.Qualification;
        employee.AvatarFileName = updates.AvatarFileName;
        employee.UpdatedAt = DateTime.UtcNow;

        await _employeeRepository.UpdateAsync(employee, cancellationToken);
        await _employeeRepository.SaveChangesAsync(cancellationToken);
        return employee;
    }

    public async Task<bool> DeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var deleted = await _employeeRepository.SoftDeleteAsync(id, cancellationToken);
        if (deleted)
        {
            await _employeeRepository.SaveChangesAsync(cancellationToken);
        }

        return deleted;
    }

    public Task<IReadOnlyList<Employee>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => _employeeRepository.GetDeletedAsync(search, cancellationToken);

    public async Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var restored = await _employeeRepository.RestoreAsync(id, cancellationToken);
        if (restored)
        {
            await _employeeRepository.SaveChangesAsync(cancellationToken);
        }

        return restored;
    }
}
