using System.Net;
using System.Net.Http.Json;
using Baya.Application.Contracts.Identity;
using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.Catalog;
using Baya.Domain.Entities.Geography;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.Payments;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using BookingEntity = Baya.Domain.Entities.Booking.Booking;

namespace Baya.Test.Api;

public class AdminRefundsApiTests(BayaApiFactory factory) : IClassFixture<BayaApiFactory>
{
    [Fact]
    public async Task Create_Unauthenticated_Returns401()
    {
        var client = factory.CreateClient();
        var response = await client.PostAsJsonAsync("/api/v1/admin_refunds", new { bookingId = 1, refundPercentage = 1m });
        Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    [Fact]
    public async Task Create_InvalidBookingId_Returns400()
    {
        var admin = factory.CreateClient();
        await AdminTestClient.AuthenticateAsync(factory, admin, "09131901401");

        var response = await admin.PostAsJsonAsync("/api/v1/admin_refunds", new { bookingId = 0, refundPercentage = 1m });
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    [Fact]
    public async Task Create_full_refund_posts_balanced_reversal_then_over_refund_conflicts()
    {
        var admin = factory.CreateClient();
        await AdminTestClient.AuthenticateAsync(factory, admin, "09131901402");
        var bookingId = await SeedCapturedBookingAsync(factory, "09131901403");

        var create = await admin.PostAsJsonAsync("/api/v1/admin_refunds", new { bookingId, refundPercentage = 1m });
        Assert.Equal(HttpStatusCode.OK, create.StatusCode);
        var data = await AuthTestClient.ReadDataAsync(create);
        Assert.Equal("succeeded", data.GetProperty("status").GetString());
        Assert.Equal("10000000", data.GetProperty("amount").GetString());

        using (var scope = factory.Services.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            var legs = db.Set<LedgerEntry>().AsNoTracking()
                .Where(l => l.BookingId == bookingId && l.SourceRefType == LedgerSourceRefType.Refund).ToList();
            Assert.Equal(5, legs.Count);
            Assert.Equal(
                legs.Where(l => l.Direction == LedgerDirection.Debit).Sum(l => l.AmountIrr),
                legs.Where(l => l.Direction == LedgerDirection.Credit).Sum(l => l.AmountIrr));
        }

        // A second refund on the fully-refunded booking pushes over captured → 409.
        var again = await admin.PostAsJsonAsync("/api/v1/admin_refunds", new { bookingId, refundPercentage = 0.5m });
        Assert.Equal(HttpStatusCode.Conflict, again.StatusCode);
    }

    [Fact]
    public async Task List_AsAdmin_ReturnsPagedEnvelope()
    {
        var admin = factory.CreateClient();
        await AdminTestClient.AuthenticateAsync(factory, admin, "09131901404");

        var response = await admin.GetAsync("/api/v1/admin_refunds?status=succeeded&page=1&pageSize=20");

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        var data = await AuthTestClient.ReadDataAsync(response);
        Assert.True(data.GetProperty("total").GetInt32() >= 0);
    }

    /// <summary>Seeds a confirmed booking (gross 10M, commission 1.5M) with a captured card transaction, owned
    /// by the given customer phone. Returns the booking id.</summary>
    internal static async Task<long> SeedCapturedBookingAsync(BayaApiFactory factory, string customerPhone)
    {
        using var scope = factory.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        var userManager = scope.ServiceProvider.GetRequiredService<IAppUserManager>();

        var customerUser = await userManager.GetUserByPhoneNumber(customerPhone);
        if (customerUser is null)
        {
            await userManager.CreateUser(new User { UserName = $"cust_{Guid.NewGuid():N}", PhoneNumber = customerPhone });
            customerUser = await userManager.GetUserByPhoneNumber(customerPhone);
        }

        var customer = db.Set<CustomerProfile>().FirstOrDefault(c => c.UserId == customerUser!.Id);
        if (customer is null)
        {
            customer = new CustomerProfile { UserId = customerUser!.Id };
            db.Set<CustomerProfile>().Add(customer);
            db.SaveChanges();
        }

        var province = new Province { NameFa = "ت", NameEn = "T", SortOrder = 1, IsActive = true };
        db.Set<Province>().Add(province);
        db.SaveChanges();
        var city = new City { ProvinceId = province.Id, NameFa = "ت", NameEn = "T", SortOrder = 1, IsActive = true };
        db.Set<City>().Add(city);
        var category = new ServiceCategory { NameFa = "س", NameEn = "E", SortOrder = 1, IsActive = true };
        db.Set<ServiceCategory>().Add(category);
        db.SaveChanges();

        var patient = new Patient { CustomerId = customer.Id, DisplayName = "پ", FirstName = "ح", LastName = "ر", Gender = "male", IsActive = true };
        db.Set<Patient>().Add(patient);
        var address = new CustomerAddress
        {
            CustomerId = customer.Id, CityId = city.Id, Title = "خ", AddressLine = "خیابان",
            PostalCode = "1111111111", RecipientName = "ع", RecipientPhone = customerPhone, IsPrimary = true
        };
        db.Set<CustomerAddress>().Add(address);
        var nurseUser = new User { UserName = $"nurse_{Guid.NewGuid():N}", PhoneNumber = $"0912{Random.Shared.Next(1000000, 9999999)}", Gender = "female", Name = "ز", FamilyName = "ا", IsActive = true };
        db.Users.Add(nurseUser);
        db.SaveChanges();
        var nurse = new NurseProfile { UserId = nurseUser.Id };
        nurse.MarkVerified();
        nurse.SetAcceptingBookings(true);
        db.Set<NurseProfile>().Add(nurse);
        db.SaveChanges();

        var variant = new NurseServiceVariant
        {
            NurseId = nurse.Id, ServiceCategoryId = category.Id, Price = 10_000_000, PriceUnit = "per_day",
            SessionCount = null, DisplayName = "مراقبت", OptionSetHash = $"hash-{Guid.NewGuid():N}", IsActive = true
        };
        db.Set<NurseServiceVariant>().Add(variant);
        db.SaveChanges();

        var request = new BookingRequest
        {
            CustomerId = customer.Id, NurseId = nurse.Id, PatientId = patient.Id, VariantId = variant.Id,
            CustomerAddressId = address.Id, RequiredCaregiverGender = CaregiverGender.Any,
            RequestedDate = new DateOnly(2026, 8, 1), RequestedTimeStart = new TimeOnly(9, 0), RequestedTimeEnd = new TimeOnly(13, 0),
            CustomerNotes = "n", NurseResponseDeadlineAt = new DateTime(2030, 1, 1, 0, 0, 0, DateTimeKind.Utc)
        };
        db.Set<BookingRequest>().Add(request);
        db.SaveChanges();
        request.Accept(new DateTime(2030, 1, 1, 0, 0, 0, DateTimeKind.Utc));
        db.SaveChanges();

        var booking = new BookingEntity
        {
            BookingRequestId = request.Id, CustomerId = customer.Id, NurseId = nurse.Id, PatientId = patient.Id,
            VariantId = variant.Id, CustomerAddressId = address.Id, VariantSnapshotJson = "{}", AddressSnapshotJson = "{}",
            GrossPriceIrr = 10_000_000, BalinyaarCommissionIrr = 1_500_000, PlatformFeeRate = 0.15m,
            NursePayoutAmount = 8_500_000, SessionCount = 1,
            ScheduledDate = new DateOnly(2026, 8, 1), ScheduledTimeStart = new TimeOnly(9, 0), ScheduledTimeEnd = new TimeOnly(13, 0)
        };
        booking.TransitionTo(BookingStatus.Confirmed, new DateTime(2026, 8, 1, 0, 0, 0, DateTimeKind.Utc));
        db.Set<BookingEntity>().Add(booking);
        db.SaveChanges();

        var gateway = new PaymentGateway { ProviderCode = "zarinpal", Type = PaymentGatewayType.Standard, DisplayName = "GW", ConfigJson = "{}", IsActive = true, Priority = 0 };
        db.Set<PaymentGateway>().Add(gateway);
        db.SaveChanges();

        var txn = new PaymentTransaction
        {
            BookingRequestId = request.Id, CustomerId = customer.Id, GatewayId = gateway.Id,
            Amount = 10_000_000, GatewayReferenceCode = $"ref-{Guid.NewGuid():N}"
        };
        txn.MarkSucceeded(booking.Id, "ok", null);
        db.Set<PaymentTransaction>().Add(txn);
        db.SaveChanges();

        return booking.Id;
    }
}
