using Microsoft.EntityFrameworkCore;
using OmsorgCore.Application.Abstractions;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Infrastructure.Persistence;

namespace OmsorgCore.Infrastructure.Repositories;

public class EmployeeRepository : IEmployeeRepository
{
    private readonly OmsorgCoreDbContext _db;

    public EmployeeRepository(OmsorgCoreDbContext db)
    {
        _db = db;
    }

    public Task<Employee?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => _db.Employees.FirstOrDefaultAsync(e => e.Id == id, cancellationToken);

    public async Task<IReadOnlyList<Employee>> GetAllAsync(CancellationToken cancellationToken = default)
        => await _db.Employees.AsNoTracking().ToListAsync(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 = null)
    {
        var query = _db.Employees.AsNoTracking().Where(e => !e.IsDeleted);

        if (restrictToEmployeeId.HasValue)
        {
            query = query.Where(e => e.Id == restrictToEmployeeId.Value);
        }

        if (!string.IsNullOrWhiteSpace(search))
        {
            var pattern = $"%{search.Trim()}%";
            query = query.Where(e =>
                EF.Functions.ILike(e.FirstName, pattern) ||
                EF.Functions.ILike(e.LastName, pattern) ||
                (e.Email != null && EF.Functions.ILike(e.Email, pattern)));
        }

        if (!string.IsNullOrWhiteSpace(status))
        {
            query = query.Where(e => e.Status == status);
        }

        if (!string.IsNullOrWhiteSpace(employmentType))
        {
            query = query.Where(e => e.EmploymentType == employmentType);
        }

        var totalCount = await query.CountAsync(cancellationToken);

        var items = await query
            .OrderBy(e => e.LastName)
            .ThenBy(e => e.FirstName)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(cancellationToken);

        return (items, totalCount);
    }

    public async Task AddAsync(Employee employee, CancellationToken cancellationToken = default)
        => await _db.Employees.AddAsync(employee, cancellationToken);

    public Task UpdateAsync(Employee employee, CancellationToken cancellationToken = default)
    {
        _db.Employees.Update(employee);
        return Task.CompletedTask;
    }

    public async Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var employee = await _db.Employees.FirstOrDefaultAsync(e => e.Id == id, cancellationToken);
        if (employee is null)
        {
            return false;
        }

        employee.IsDeleted = true;
        employee.DeletedAt = DateTime.UtcNow;
        return true;
    }

    public async Task<IReadOnlyList<Employee>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
    {
        var query = _db.Employees.IgnoreQueryFilters().AsNoTracking().Where(e => e.IsDeleted);

        if (!string.IsNullOrWhiteSpace(search))
        {
            var pattern = $"%{search.Trim()}%";
            query = query.Where(e =>
                EF.Functions.ILike(e.FirstName, pattern) ||
                EF.Functions.ILike(e.LastName, pattern) ||
                (e.Email != null && EF.Functions.ILike(e.Email, pattern)));
        }

        return await query.OrderByDescending(e => e.DeletedAt).ToListAsync(cancellationToken);
    }

    public async Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var employee = await _db.Employees.IgnoreQueryFilters().FirstOrDefaultAsync(e => e.Id == id && e.IsDeleted, cancellationToken);
        if (employee is null)
        {
            return false;
        }

        employee.IsDeleted = false;
        employee.DeletedAt = null;
        return true;
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => _db.SaveChangesAsync(cancellationToken);
}
