#nullable enable
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Contracts.Search;
using Baya.Application.Models.Common;
using Baya.Application.Models.Geography;
using Baya.Domain.Entities.Geography;
using Baya.Domain.Entities.User;
using Mediator;

namespace Baya.Application.Features.ServiceAreas.Commands.AddNurseServiceArea;

internal sealed class AddNurseServiceAreaCommandHandler(ICurrentUser currentUser, IUnitOfWork unitOfWork, ISearchIndexMaintainer searchIndex)
    : IRequestHandler<AddNurseServiceAreaCommand, OperationResult<NurseServiceAreaDto>>
{
    public async ValueTask<OperationResult<NurseServiceAreaDto>> Handle(AddNurseServiceAreaCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<NurseServiceAreaDto>.UnauthorizedResult("Not authenticated.");

        if (currentUser.Roles?.Contains(RoleNames.Nurse) != true)
            return OperationResult<NurseServiceAreaDto>.ForbiddenResult("Only a nurse can declare a service area.");

        var nurseId = await unitOfWork.NurseProfileRepository.GetProfileIdByUserIdAsync(userId, cancellationToken);
        if (nurseId is not { } nid)
            return OperationResult<NurseServiceAreaDto>.FailureResult("No nurse profile exists yet. Create your profile first.");

        var city = await unitOfWork.GeoRepository.GetCityAsync(request.CityId, cancellationToken);
        if (city is null || !await unitOfWork.GeoRepository.IsCityActiveAsync(request.CityId, cancellationToken))
            return OperationResult<NurseServiceAreaDto>.FailureResult(nameof(request.CityId), "City not found or inactive.");

        District? district = null;
        if (request.DistrictId is { } districtId)
        {
            if (!await unitOfWork.GeoRepository.IsDistrictInActiveCityAsync(districtId, request.CityId, cancellationToken))
                return OperationResult<NurseServiceAreaDto>.FailureResult(nameof(request.DistrictId), "District not found in this city, or inactive.");

            district = await unitOfWork.GeoRepository.GetDistrictAsync(districtId, cancellationToken);
        }

        // Whole-city and city+district duplicates are both rejected — the pre-check returns a clean 409;
        // the filtered unique-index pair is the DB backstop.
        if (await unitOfWork.NurseServiceAreaRepository.DuplicateExistsAsync(nid, request.CityId, request.DistrictId, cancellationToken))
            return OperationResult<NurseServiceAreaDto>.ConflictResult(
                request.DistrictId is null
                    ? "You already cover this whole city."
                    : "You already cover this district.");

        var area = new NurseServiceArea
        {
            NurseId = nid,
            CityId = request.CityId,
            DistrictId = request.DistrictId,
            IsActive = true
        };

        await unitOfWork.NurseServiceAreaRepository.AddAsync(area, cancellationToken);

        // Fan the newly-covered area out into nurse_search_index: one row per active variant, in the same
        // transaction. The area itself may still be uncommitted, so its city/district come from the request.
        await searchIndex.FanOutServiceAreaAsync(nid, request.CityId, request.DistrictId, cancellationToken);
        await unitOfWork.CommitAsync();

        return OperationResult<NurseServiceAreaDto>.SuccessResult(new NurseServiceAreaDto(
            area.Id,
            city.Id,
            city.NameFa,
            city.NameEn,
            area.DistrictId,
            district?.NameFa,
            district?.NameEn,
            area.DistrictId is null,
            area.IsActive));
    }
}
