using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.Invoices;
using Baya.Domain.Entities.PartnerCenters;
using Baya.Infrastructure.Persistence;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Baya.Tests.Setup.Setups;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using BookingEntity = Baya.Domain.Entities.Booking.Booking;

namespace Baya.Test.Foundation.PartnerCenters;

/// <summary>
/// The merchant-of-record resolver: <c>GetCenterForBooking</c> must return the sponsoring center as the invoice
/// issuer + settlement target ONLY when that center is merchant-of-record, and <c>platform</c> otherwise (incl.
/// an unsponsored nurse). Foreign keys are disabled so the test can seed just the three rows the join touches.
/// </summary>
public sealed class CenterForBookingTests : IDisposable
{
    private readonly SqliteConnection _connection;
    private readonly ApplicationDbContext _db;
    private readonly UnitOfWork _unitOfWork;

    public CenterForBookingTests()
    {
        _connection = new SqliteConnection("DataSource=:memory:;Foreign Keys=False");
        _connection.Open();
        var options = new DbContextOptionsBuilder<ApplicationDbContext>().UseSqlite(_connection).Options;
        _db = new ApplicationDbContext(options, TestFieldEncryptor.Instance);
        _db.Database.EnsureCreated();
        _unitOfWork = new UnitOfWork(_db);
    }

    private long SeedNurse(long? partnerCenterId)
    {
        var nurse = new NurseProfile
        {
            UserId = 0,
            Bio = string.Empty,
            EducationLevel = string.Empty,
            EducationField = string.Empty,
            SpecializationsJson = "[]",
            PartnerCenterId = partnerCenterId
        };
        _db.Set<NurseProfile>().Add(nurse);
        _db.SaveChanges();
        return nurse.Id;
    }

    private long SeedBooking(long nurseId)
    {
        var booking = new BookingEntity
        {
            BookingRequestId = 0,
            CustomerId = 0,
            NurseId = nurseId,
            PatientId = 0,
            VariantId = 0,
            CustomerAddressId = 0,
            VariantSnapshotJson = "{}",
            AddressSnapshotJson = "{}",
            GrossPriceIrr = 0,
            BalinyaarCommissionIrr = 0,
            NursePayoutAmount = 0
        };
        _db.Set<BookingEntity>().Add(booking);
        _db.SaveChanges();
        return booking.Id;
    }

    private long SeedCenter(bool merchantOfRecord)
    {
        var center = new PartnerCenter
        {
            Name = "Center",
            MohEstablishmentPermitNo = "MOH-1",
            AdminUserId = 0,
            IsMerchantOfRecord = merchantOfRecord
        };
        _db.Set<PartnerCenter>().Add(center);
        _db.SaveChanges();
        return center.Id;
    }

    [Fact]
    public async Task MerchantOfRecordCenter_IsTheIssuerAndSettlementTarget()
    {
        var centerId = SeedCenter(merchantOfRecord: true);
        var nurseId = SeedNurse(centerId);
        var bookingId = SeedBooking(nurseId);

        var result = await _unitOfWork.PartnerCenterRepository.ResolveCenterForBookingAsync(bookingId, CancellationToken.None);

        Assert.NotNull(result);
        Assert.Equal(InvoiceIssuingEntityType.PartnerCenter, result!.IssuingEntityType);
        Assert.Equal(centerId, result.PartnerCenterId);
        Assert.True(result.IsMerchantOfRecord);
    }

    [Fact]
    public async Task NonMerchantSponsor_FallsBackToPlatform()
    {
        var centerId = SeedCenter(merchantOfRecord: false);
        var nurseId = SeedNurse(centerId);
        var bookingId = SeedBooking(nurseId);

        var result = await _unitOfWork.PartnerCenterRepository.ResolveCenterForBookingAsync(bookingId, CancellationToken.None);

        Assert.NotNull(result);
        Assert.Equal(InvoiceIssuingEntityType.Platform, result!.IssuingEntityType);
        Assert.Null(result.PartnerCenterId);
    }

    [Fact]
    public async Task UnsponsoredNurse_IsPlatform()
    {
        var nurseId = SeedNurse(partnerCenterId: null);
        var bookingId = SeedBooking(nurseId);

        var result = await _unitOfWork.PartnerCenterRepository.ResolveCenterForBookingAsync(bookingId, CancellationToken.None);

        Assert.NotNull(result);
        Assert.Equal(InvoiceIssuingEntityType.Platform, result!.IssuingEntityType);
        Assert.Null(result.PartnerCenterId);
    }

    [Fact]
    public async Task MissingBooking_ReturnsNull()
    {
        var result = await _unitOfWork.PartnerCenterRepository.ResolveCenterForBookingAsync(999, CancellationToken.None);
        Assert.Null(result);
    }

    public void Dispose()
    {
        _db.Dispose();
        _connection.Dispose();
    }
}
