using MediatR;
using IamService.Domain.AggregatesModel.OrganizationAggregate;

namespace IamService.API.Application.Queries.Organizations;

/// <summary>
/// EN: Handler for GetOrganizationByIdQuery.
/// VI: Handler cho GetOrganizationByIdQuery.
/// </summary>
public class GetOrganizationByIdQueryHandler : IRequestHandler<GetOrganizationByIdQuery, OrganizationDto?>
{
    private readonly IOrganizationRepository _organizationRepository;

    public GetOrganizationByIdQueryHandler(IOrganizationRepository organizationRepository)
    {
        _organizationRepository = organizationRepository;
    }

    public async Task<OrganizationDto?> Handle(GetOrganizationByIdQuery request, CancellationToken cancellationToken)
    {
        var organization = await _organizationRepository.GetByIdAsync(request.Id, cancellationToken);

        return organization == null ? null : MapToDto(organization);
    }

    private static OrganizationDto MapToDto(Organization org) => new(
        org.Id,
        org.Name,
        org.Slug,
        org.Description,
        org.ParentOrganizationId,
        org.Status.Name,
        org.CreatedAt,
        org.UpdatedAt);
}

/// <summary>
/// EN: Handler for GetOrganizationBySlugQuery.
/// VI: Handler cho GetOrganizationBySlugQuery.
/// </summary>
public class GetOrganizationBySlugQueryHandler : IRequestHandler<GetOrganizationBySlugQuery, OrganizationDto?>
{
    private readonly IOrganizationRepository _organizationRepository;

    public GetOrganizationBySlugQueryHandler(IOrganizationRepository organizationRepository)
    {
        _organizationRepository = organizationRepository;
    }

    public async Task<OrganizationDto?> Handle(GetOrganizationBySlugQuery request, CancellationToken cancellationToken)
    {
        var organization = await _organizationRepository.GetBySlugAsync(request.Slug, cancellationToken);

        return organization == null ? null : MapToDto(organization);
    }

    private static OrganizationDto MapToDto(Organization org) => new(
        org.Id,
        org.Name,
        org.Slug,
        org.Description,
        org.ParentOrganizationId,
        org.Status.Name,
        org.CreatedAt,
        org.UpdatedAt);
}

/// <summary>
/// EN: Handler for GetOrganizationHierarchyQuery.
/// VI: Handler cho GetOrganizationHierarchyQuery.
/// </summary>
public class GetOrganizationHierarchyQueryHandler : IRequestHandler<GetOrganizationHierarchyQuery, IEnumerable<OrganizationDto>>
{
    private readonly IOrganizationRepository _organizationRepository;

    public GetOrganizationHierarchyQueryHandler(IOrganizationRepository organizationRepository)
    {
        _organizationRepository = organizationRepository;
    }

    public async Task<IEnumerable<OrganizationDto>> Handle(GetOrganizationHierarchyQuery request, CancellationToken cancellationToken)
    {
        var organizations = await _organizationRepository.GetHierarchyAsync(request.RootId, cancellationToken);

        return organizations.Select(MapToDto);
    }

    private static OrganizationDto MapToDto(Organization org) => new(
        org.Id,
        org.Name,
        org.Slug,
        org.Description,
        org.ParentOrganizationId,
        org.Status.Name,
        org.CreatedAt,
        org.UpdatedAt);
}

/// <summary>
/// EN: Handler for GetChildOrganizationsQuery.
/// VI: Handler cho GetChildOrganizationsQuery.
/// </summary>
public class GetChildOrganizationsQueryHandler : IRequestHandler<GetChildOrganizationsQuery, IEnumerable<OrganizationDto>>
{
    private readonly IOrganizationRepository _organizationRepository;

    public GetChildOrganizationsQueryHandler(IOrganizationRepository organizationRepository)
    {
        _organizationRepository = organizationRepository;
    }

    public async Task<IEnumerable<OrganizationDto>> Handle(GetChildOrganizationsQuery request, CancellationToken cancellationToken)
    {
        var children = await _organizationRepository.GetChildrenAsync(request.ParentId, cancellationToken);

        return children.Select(MapToDto);
    }

    private static OrganizationDto MapToDto(Organization org) => new(
        org.Id,
        org.Name,
        org.Slug,
        org.Description,
        org.ParentOrganizationId,
        org.Status.Name,
        org.CreatedAt,
        org.UpdatedAt);
}
