using Meezi.API.Models.Public;
using Meezi.Core.Entities;
using Meezi.Core.Enums;
using Meezi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace Meezi.API.Services;

public interface IReservationService
{
    Task<IReadOnlyList<ReservationDto>> GetReservationsAsync(
        string cafeId,
        DateOnly? date,
        ReservationStatus? status,
        CancellationToken cancellationToken = default);

    Task<ReservationDto?> CreateAsync(
        string cafeId,
        CreateReservationRequest request,
        CancellationToken cancellationToken = default);

    Task<ReservationDto?> UpdateStatusAsync(
        string cafeId,
        string reservationId,
        ReservationStatus status,
        CancellationToken cancellationToken = default);
}

public class ReservationService : IReservationService
{
    private readonly AppDbContext _db;
    private readonly IKdsNotifier _kdsNotifier;

    public ReservationService(AppDbContext db, IKdsNotifier kdsNotifier)
    {
        _db = db;
        _kdsNotifier = kdsNotifier;
    }

    public async Task<IReadOnlyList<ReservationDto>> GetReservationsAsync(
        string cafeId,
        DateOnly? date,
        ReservationStatus? status,
        CancellationToken cancellationToken = default)
    {
        var query = _db.TableReservations
            .Include(r => r.Table)
            .Where(r => r.CafeId == cafeId);
        if (date.HasValue)
            query = query.Where(r => r.Date == date.Value);
        if (status.HasValue)
            query = query.Where(r => r.Status == status.Value);

        var list = await query
            .OrderBy(r => r.Date)
            .ThenBy(r => r.Time)
            .ToListAsync(cancellationToken);

        return list.Select(Map).ToList();
    }

    public async Task<ReservationDto?> CreateAsync(
        string cafeId,
        CreateReservationRequest request,
        CancellationToken cancellationToken = default)
    {
        if (!string.IsNullOrEmpty(request.TableId))
        {
            var tableOk = await _db.Tables.AnyAsync(
                t => t.Id == request.TableId && t.CafeId == cafeId,
                cancellationToken);
            if (!tableOk) return null;
        }

        var entity = new TableReservation
        {
            CafeId = cafeId,
            TableId = request.TableId,
            GuestName = request.GuestName.Trim(),
            GuestPhone = request.GuestPhone.Trim(),
            Date = request.Date,
            Time = request.Time,
            PartySize = request.PartySize,
            Notes = request.Notes,
            Status = ReservationStatus.Confirmed
        };

        _db.TableReservations.Add(entity);
        await _db.SaveChangesAsync(cancellationToken);

        if (!string.IsNullOrEmpty(entity.TableId))
            await _kdsNotifier.NotifyTableStatusChangedAsync(cafeId, cancellationToken);

        var loaded = await _db.TableReservations
            .Include(r => r.Table)
            .FirstAsync(r => r.Id == entity.Id, cancellationToken);

        return Map(loaded);
    }

    public async Task<ReservationDto?> UpdateStatusAsync(
        string cafeId,
        string reservationId,
        ReservationStatus status,
        CancellationToken cancellationToken = default)
    {
        var entity = await _db.TableReservations
            .Include(r => r.Table)
            .FirstOrDefaultAsync(r => r.Id == reservationId && r.CafeId == cafeId, cancellationToken);
        if (entity is null) return null;

        entity.Status = status;
        await _db.SaveChangesAsync(cancellationToken);

        if (!string.IsNullOrEmpty(entity.TableId))
            await _kdsNotifier.NotifyTableStatusChangedAsync(cafeId, cancellationToken);

        return Map(entity);
    }

    internal static ReservationDto Map(TableReservation r) => new(
        r.Id,
        r.CafeId,
        r.TableId,
        r.Table?.Number,
        r.GuestName,
        r.GuestPhone,
        r.Date,
        r.Time,
        r.PartySize,
        r.Status,
        r.Notes);
}
