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

namespace OmsorgCore.Application.Services;

public class ContractService : IContractService
{
    private readonly IContractRepository _contractRepository;
    private readonly IPermissionService _permissionService;
    private readonly ICurrentUserService _currentUserService;

    public ContractService(
        IContractRepository contractRepository,
        IPermissionService permissionService,
        ICurrentUserService currentUserService)
    {
        _contractRepository = contractRepository;
        _permissionService = permissionService;
        _currentUserService = currentUserService;
    }

    public Task<IReadOnlyList<Contract>> GetAllAsync(CancellationToken cancellationToken = default)
        => _contractRepository.GetAllAsync(cancellationToken);

    public async Task<(IReadOnlyList<Contract> Items, int TotalCount)> GetPagedAsync(
        string? search,
        string? status,
        Guid? employeeId,
        Guid? facilityId,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        Guid? ownRestriction = await ResolveOwnScopeRestrictionAsync(cancellationToken);
        // Own-Scope erzwingt die eigene EmployeeId und überschreibt einen ggf. angeforderten
        // fremden employeeId-Filter - sonst könnte ein Own-User über den Query-Parameter fremde
        // Verträge abfragen.
        Guid? effectiveEmployeeId = ownRestriction ?? employeeId;
        return await _contractRepository.GetPagedAsync(search, status, effectiveEmployeeId, facilityId, page, pageSize, cancellationToken);
    }

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

        Guid? ownRestriction = await ResolveOwnScopeRestrictionAsync(cancellationToken);
        if (ownRestriction.HasValue && contract.EmployeeId != ownRestriction.Value)
        {
            return null;
        }

        return contract;
    }

    /// <summary>Own-Scope-Filterung, siehe EmployeeService.ResolveOwnScopeRestrictionAsync (analoges Muster für ModuleType.Contracts).</summary>
    private async Task<Guid?> ResolveOwnScopeRestrictionAsync(CancellationToken cancellationToken)
    {
        if (_currentUserService.UserId is not { } userId)
        {
            return null;
        }

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

    public async Task<Contract> CreateAsync(Contract contract, CancellationToken cancellationToken = default)
    {
        await _contractRepository.AddAsync(contract, cancellationToken);
        await _contractRepository.SaveChangesAsync(cancellationToken);
        return contract;
    }

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

        contract.ContractType = updates.ContractType;
        contract.EmployeeId = updates.EmployeeId;
        contract.FacilityId = updates.FacilityId;
        contract.StartDate = updates.StartDate;
        contract.EndDate = updates.EndDate;
        contract.Status = updates.Status;
        contract.WeeklyHours = updates.WeeklyHours;
        contract.HourlyWage = updates.HourlyWage;
        contract.AllowancesDescription = updates.AllowancesDescription;
        contract.OvertimeRules = updates.OvertimeRules;
        contract.VacationDaysPerYear = updates.VacationDaysPerYear;
        contract.ProbationPeriodMonths = updates.ProbationPeriodMonths;
        contract.UpdatedAt = DateTime.UtcNow;

        await _contractRepository.UpdateAsync(contract, cancellationToken);
        await _contractRepository.SaveChangesAsync(cancellationToken);
        return contract;
    }

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

        return deleted;
    }

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

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

        return restored;
    }
}
