using System.Globalization;
using Baya.Application.Common;
using Baya.Application.Contracts.Common;
using Baya.Application.Features.Bookings.Commands.CancelBooking;
using Baya.Application.Features.Bookings.Commands.ConvertRequestToBooking;
using Baya.Domain.Entities.Booking;
using Microsoft.EntityFrameworkCore;
using NSubstitute;
using BookingEntity = Baya.Domain.Entities.Booking.Booking;

namespace Baya.Test.Foundation.Bookings;

public class CancellationTests
{
    // Well before the 2026-08-01 engagement start → the ≥24h customer tier (100% refund).
    private static readonly DateTimeOffset Now = new(2026, 7, 6, 10, 0, 0, TimeSpan.Zero);

    private static async Task<long> ConvertAsync(BookingsTestHost host, int sessions)
    {
        var variantId = host.AddVariant(sessionCount: sessions, price: 5_000_000);
        var requestId = host.AddAcceptedRequest(variantId);
        var handler = new ConvertRequestToBookingCommandHandler(
            host.AsCustomer(), host.UnitOfWork, host.Config(), host.Clock(Now), host.Capture(),
            new VariantSnapshotSerializer(), Substitute.For<INotificationDispatcher>());
        var result = await handler.Handle(new ConvertRequestToBookingCommand(requestId), CancellationToken.None);
        return result.Result.Id;
    }

    [Fact]
    public async Task Cancel_resolves_snapshots_policy_and_refunds_unstarted_sessions()
    {
        using var host = new BookingsTestHost();
        var bookingId = await ConvertAsync(host, sessions: 3); // gross 15M, all un-started

        var handler = new CancelBookingCommandHandler(host.AsCustomer(), host.UnitOfWork, host.Clock(Now));
        var result = await handler.Handle(new CancelBookingCommand("changed plans", bookingId), CancellationToken.None);

        Assert.True(result.IsSuccess);
        Assert.Equal(CancellationPolicyCode.Standard24h, result.Result.PolicyCode);
        Assert.Equal(100m, result.Result.RefundPercentage);
        Assert.Equal(15_000_000, long.Parse(result.Result.RefundableAmountIrr, CultureInfo.InvariantCulture));
        Assert.Equal(BookingStatus.Cancelled, host.StatusOf(bookingId));

        var sessions = host.Db.Set<BookingSession>().AsNoTracking().Where(s => s.BookingId == bookingId).ToList();
        Assert.All(sessions, s => Assert.Equal(BookingSessionStatus.Cancelled, s.Status));
    }

    [Fact]
    public async Task Cancellation_snapshot_is_immune_to_a_later_policy_edit()
    {
        using var host = new BookingsTestHost();
        var bookingId = await ConvertAsync(host, sessions: 1);

        var handler = new CancelBookingCommandHandler(host.AsCustomer(), host.UnitOfWork, host.Clock(Now));
        await handler.Handle(new CancelBookingCommand("changed plans", bookingId), CancellationToken.None);

        // Editing the underlying policy afterward must not change the frozen snapshot.
        var policy = host.Db.Set<CancellationPolicy>().Single(p => p.Code == CancellationPolicyCode.Standard24h);
        policy.RefundPercentage = 10m;
        host.Db.SaveChanges();

        var booking = host.Db.Set<BookingEntity>().AsNoTracking().Single(b => b.Id == bookingId);
        Assert.Equal(100m, booking.CancellationRefundPercentage);
        Assert.Equal(CancellationPolicyCode.Standard24h, booking.CancellationPolicyCode);
    }
}
