using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.Catalog;
using Baya.Domain.Entities.Geography;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.Persistence;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Baya.Tests.Setup.Setups;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace Baya.Test.Foundation.Booking;

/// <summary>
/// A self-contained SQLite host exercising the real EF model (schema, filtered indexes, query filters) for
/// the booking-request repository, expiry sweep, and queries end-to-end. Seeds one customer (user + profile
/// + patient + address) and one bookable nurse (user + profile + active variant), and lets a test create
/// requests in a chosen state and drive the real <see cref="UnitOfWork"/>.
/// </summary>
public sealed class BookingTestHost : IDisposable
{
    private readonly SqliteConnection _connection;
    public ApplicationDbContext Db { get; }
    public UnitOfWork UnitOfWork { get; }

    public long CustomerId { get; }
    public int CustomerUserId { get; }
    public long NurseId { get; }
    public int NurseUserId { get; }
    public long PatientId { get; }
    public long AddressId { get; }
    public long VariantId { get; }

    public BookingTestHost()
    {
        _connection = new SqliteConnection("DataSource=:memory:");
        _connection.Open();

        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseSqlite(_connection)
            .Options;

        Db = new ApplicationDbContext(options, TestFieldEncryptor.Instance);
        Db.Database.EnsureCreated();
        UnitOfWork = new UnitOfWork(Db);

        var province = new Province { NameFa = "تهران", NameEn = "Tehran", SortOrder = 1, IsActive = true };
        Db.Set<Province>().Add(province);
        Db.SaveChanges();

        var city = new City { ProvinceId = province.Id, NameFa = "تهران", NameEn = "Tehran", SortOrder = 1, IsActive = true };
        Db.Set<City>().Add(city);
        Db.SaveChanges();

        var category = new ServiceCategory { NameFa = "سالمند", NameEn = "Elderly", SortOrder = 1, IsActive = true };
        Db.Set<ServiceCategory>().Add(category);
        Db.SaveChanges();

        var customerUser = new User { UserName = "cust1", PhoneNumber = "09120000001", Gender = "male", Name = "علی", FamilyName = "رضایی", IsActive = true };
        Db.Users.Add(customerUser);
        Db.SaveChanges();
        CustomerUserId = customerUser.Id;

        var customer = new CustomerProfile { UserId = customerUser.Id };
        Db.Set<CustomerProfile>().Add(customer);
        Db.SaveChanges();
        CustomerId = customer.Id;

        var patient = new Patient { CustomerId = customer.Id, DisplayName = "پدر", FirstName = "حسن", LastName = "رضایی", Gender = "male", IsActive = true };
        Db.Set<Patient>().Add(patient);
        Db.SaveChanges();
        PatientId = patient.Id;

        var address = new CustomerAddress
        {
            CustomerId = customer.Id, CityId = city.Id, Title = "خانه",
            AddressLine = "خیابان اول", PostalCode = "1111111111", RecipientName = "علی", RecipientPhone = "09120000001", IsPrimary = true
        };
        Db.Set<CustomerAddress>().Add(address);
        Db.SaveChanges();
        AddressId = address.Id;

        var nurseUser = new User { UserName = "nurse1", PhoneNumber = "09120000002", Gender = "female", Name = "زهرا", FamilyName = "احمدی", IsActive = true };
        Db.Users.Add(nurseUser);
        Db.SaveChanges();
        NurseUserId = nurseUser.Id;

        var nurse = new NurseProfile { UserId = nurseUser.Id };
        nurse.MarkVerified();
        nurse.SetAcceptingBookings(true);
        Db.Set<NurseProfile>().Add(nurse);
        Db.SaveChanges();
        NurseId = nurse.Id;

        var variant = new NurseServiceVariant
        {
            NurseId = nurse.Id, ServiceCategoryId = category.Id, Price = 5_000_000, PriceUnit = "per_day",
            DisplayName = "مراقبت روزانه", OptionSetHash = "hash", IsActive = true
        };
        Db.Set<NurseServiceVariant>().Add(variant);
        Db.SaveChanges();
        VariantId = variant.Id;
    }

    /// <summary>Inserts a pending request with the given response deadline (defaults far in the future).</summary>
    public BookingRequest AddPendingRequest(DateTime? responseDeadline = null)
    {
        var request = new BookingRequest
        {
            CustomerId = CustomerId,
            NurseId = NurseId,
            PatientId = PatientId,
            VariantId = VariantId,
            CustomerAddressId = AddressId,
            RequiredCaregiverGender = CaregiverGender.Any,
            RequestedDate = new DateOnly(2026, 8, 1),
            RequestedTimeStart = new TimeOnly(9, 0),
            RequestedTimeEnd = new TimeOnly(13, 0),
            CustomerNotes = "Careful with the IV.",
            NurseResponseDeadlineAt = responseDeadline ?? new DateTime(2030, 1, 1, 0, 0, 0, DateTimeKind.Utc)
        };
        Db.Set<BookingRequest>().Add(request);
        Db.SaveChanges();
        return request;
    }

    /// <summary>Inserts an accepted request whose payment window closes at <paramref name="paymentDeadline"/>.</summary>
    public BookingRequest AddAcceptedRequest(DateTime paymentDeadline)
    {
        var request = AddPendingRequest();
        request.Accept(paymentDeadline);
        Db.SaveChanges();
        return request;
    }

    public string StatusOf(long id)
        => Db.Set<BookingRequest>().AsNoTracking().Where(r => r.Id == id).Select(r => r.Status).Single();

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