using Microsoft.EntityFrameworkCore;
using IamService.Domain.AggregatesModel.OrganizationAggregate;
using IamService.Domain.SeedWork;

namespace IamService.Infrastructure.Repositories;

/// <summary>
/// EN: Repository implementation for Organization aggregate.
/// VI: Repository implementation cho Organization aggregate.
/// </summary>
public class OrganizationRepository : IOrganizationRepository
{
    private readonly IamServiceContext _context;

    public IUnitOfWork UnitOfWork => _context;

    public OrganizationRepository(IamServiceContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    /// <inheritdoc />
    public async Task<Organization?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        return await _context.Organizations
            .FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<Organization?> GetBySlugAsync(string slug, CancellationToken cancellationToken = default)
    {
        return await _context.Organizations
            .FirstOrDefaultAsync(o => EF.Property<string>(o, "_slug") == slug.ToLowerInvariant(), cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IEnumerable<Organization>> GetHierarchyAsync(Guid rootId, CancellationToken cancellationToken = default)
    {
        // EN: Get the root organization and all its descendants using recursive CTE
        // VI: Lấy tổ chức gốc và tất cả con cháu sử dụng recursive CTE
        var results = new List<Organization>();

        var root = await GetByIdAsync(rootId, cancellationToken);
        if (root != null)
        {
            results.Add(root);
            await GetChildrenRecursiveAsync(rootId, results, cancellationToken);
        }

        return results;
    }

    /// <inheritdoc />
    public async Task<IEnumerable<Organization>> GetChildrenAsync(Guid parentId, CancellationToken cancellationToken = default)
    {
        return await _context.Organizations
            .Where(o => EF.Property<Guid?>(o, "_parentOrganizationId") == parentId)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<bool> IsSlugUniqueAsync(string slug, Guid? excludeId = null, CancellationToken cancellationToken = default)
    {
        var query = _context.Organizations
            .Where(o => EF.Property<string>(o, "_slug") == slug.ToLowerInvariant());

        if (excludeId.HasValue)
        {
            query = query.Where(o => o.Id != excludeId.Value);
        }

        return !await query.AnyAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Organization Add(Organization organization)
    {
        return _context.Organizations.Add(organization).Entity;
    }

    /// <inheritdoc />
    public void Update(Organization organization)
    {
        _context.Entry(organization).State = EntityState.Modified;
    }

    /// <inheritdoc />
    public void Delete(Organization organization)
    {
        _context.Organizations.Remove(organization);
    }

    private async Task GetChildrenRecursiveAsync(
        Guid parentId,
        List<Organization> results,
        CancellationToken cancellationToken)
    {
        var children = await GetChildrenAsync(parentId, cancellationToken);

        foreach (var child in children)
        {
            results.Add(child);
            await GetChildrenRecursiveAsync(child.Id, results, cancellationToken);
        }
    }
}
