using Microsoft.EntityFrameworkCore;

public class OrderService : IOrderService
{
    private static readonly Dictionary<OrderStatus, HashSet<OrderStatus>> _allowedTransitions = new()
    {
        [OrderStatus.Pending]    = new() { OrderStatus.InProgress, OrderStatus.Cancelled },
        [OrderStatus.InProgress] = new() { OrderStatus.Resulted, OrderStatus.Cancelled },
        [OrderStatus.Resulted]   = new(),
        [OrderStatus.Cancelled]  = new(),
    };

    private readonly AppDbContext _db;
    private readonly IServiceProvider _serviceProvider;

    public OrderService(AppDbContext db, IServiceProvider serviceProvider)
    {
        _db = db;
        _serviceProvider = serviceProvider;
    }

    public async Task<Order> CreateAsync(Guid encounterId, CreateOrderRequest req)
    {
        var encounter = await _db.Encounters.FindAsync(encounterId);
        if (encounter is null)
            throw new NotFoundException("Encounter not found.", "ENCOUNTER_NOT_FOUND");

        if (encounter.Status != EncounterStatus.Active)
            throw new ConflictException(
                "Cannot create orders for a non-active encounter.",
                "ENCOUNTER_NOT_ACTIVE");

        var order = new Order
        {
            Id = Guid.NewGuid(),
            EncounterId = encounterId,
            OrderType = req.OrderType,
            Description = req.Description,
            OrderedBy = req.OrderedBy,
            Status = OrderStatus.Pending,
            OrderedAt = DateTimeOffset.UtcNow
        };
        _db.Orders.Add(order);
        await _db.SaveChangesAsync();
        return order;
    }

    public async Task<PagedResult<Order>> ListByEncounterAsync(
        Guid encounterId, OrderStatus? status, int page, int pageSize)
    {
        var query = _db.Orders
            .AsNoTracking()
            .Where(o => o.EncounterId == encounterId);

        if (status.HasValue)
            query = query.Where(o => o.Status == status.Value);

        var total = await query.CountAsync();
        var orders = await query
            .OrderByDescending(o => o.OrderedAt)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        return new PagedResult<Order>(orders, page, pageSize, total);
    }

    public async Task<Order> GetByIdAsync(Guid id)
    {
        var order = await _db.Orders
            .AsNoTracking()
            .Include(o => o.Encounter)
            .FirstOrDefaultAsync(o => o.Id == id);

        if (order is null)
            throw new NotFoundException("Order not found.", "ORDER_NOT_FOUND");

        return order;
    }

    public async Task<Order> TransitionStatusAsync(Guid id, OrderStatus targetStatus)
    {
        var order = await _db.Orders.FindAsync(id);
        if (order is null)
            throw new NotFoundException("Order not found.", "ORDER_NOT_FOUND");

        if (!_allowedTransitions[order.Status].Contains(targetStatus))
            throw new ConflictException(
                $"Transition to '{targetStatus}' is not permitted from status '{order.Status}'.",
                "ILLEGAL_ORDER_STATUS_TRANSITION");

        order.Status = targetStatus;
        if (targetStatus == OrderStatus.Resulted)
            order.ResultedAt = DateTimeOffset.UtcNow;

        await _db.SaveChangesAsync();
        return order;
    }

    public async Task<Order> RecordResultAsync(Guid id, RecordOrderResultRequest req)
    {
        var order = await _db.Orders.FindAsync(id);
        if (order is null)
            throw new NotFoundException("Order not found.", "ORDER_NOT_FOUND");

        if (order.Status == OrderStatus.Resulted)
            throw new ConflictException("Order already resulted.", "ORDER_ALREADY_RESULTED");

        if (order.Status == OrderStatus.Cancelled)
            throw new ConflictException("Cannot result a cancelled order.", "ORDER_CANCELLED");

        order.Status = OrderStatus.Resulted;
        order.ResultedAt = DateTimeOffset.UtcNow;
        order.ResultSummary = req.ResultSummary;

        await using var tx = _db.Database.CurrentTransaction is null
            ? await _db.Database.BeginTransactionAsync()
            : null;
        await _db.SaveChangesAsync();
        var bundleService = _serviceProvider.GetRequiredService<ISepsisBundleService>();
        await bundleService.OnOrderResultedAsync(order.Id);
        if (tx is not null) await tx.CommitAsync();

        return order;
    }
}