// EN: Appointment aggregate root for service bookings.
// VI: Aggregate root Appointment cho đặt lịch dịch vụ.

using BookingService.Domain.Events;
using BookingService.Domain.Exceptions;
using BookingService.Domain.SeedWork;

namespace BookingService.Domain.AggregatesModel.AppointmentAggregate;

/// <summary>
/// EN: Appointment aggregate root - represents a service booking.
/// VI: Aggregate root Appointment - đại diện cho đặt lịch dịch vụ.
/// </summary>
public class Appointment : Entity, IAggregateRoot
{
    private Guid _shopId;
    private Guid? _customerId;
    private Guid? _staffId;
    private Guid? _resourceId;
    private Guid _serviceId;
    private DateTime _startTime;
    private DateTime _endTime;
    private string _status = null!; // Scheduled, Confirmed, InProgress, Completed, Cancelled
    private DateTime _createdAt;

    public Guid ShopId => _shopId;
    public Guid? CustomerId => _customerId;
    public Guid? StaffId => _staffId;
    public Guid? ResourceId => _resourceId;
    public Guid ServiceId => _serviceId;
    public DateTime StartTime => _startTime;
    public DateTime EndTime => _endTime;
    public string Status => _status;
    public DateTime CreatedAt => _createdAt;

    protected Appointment()
    {
    }

    public Appointment(
        Guid shopId,
        Guid serviceId,
        DateTime startTime,
        DateTime endTime,
        Guid? customerId = null,
        Guid? staffId = null,
        Guid? resourceId = null)
    {
        if (shopId == Guid.Empty)
            throw new DomainException("Shop ID cannot be empty");
        if (serviceId == Guid.Empty)
            throw new DomainException("Service ID cannot be empty");
        if (endTime <= startTime)
            throw new DomainException("End time must be after start time");

        Id = Guid.NewGuid();
        _shopId = shopId;
        _serviceId = serviceId;
        _startTime = startTime;
        _endTime = endTime;
        _customerId = customerId;
        _staffId = staffId;
        _resourceId = resourceId;
        _status = "Scheduled";
        _createdAt = DateTime.UtcNow;

        AddDomainEvent(new AppointmentCreatedDomainEvent(this));
    }

    public void Confirm()
    {
        if (_status != "Scheduled")
            throw new DomainException($"Cannot confirm appointment with status {_status}");

        _status = "Confirmed";
    }

    public void MarkAsInProgress()
    {
        if (_status != "Confirmed")
            throw new DomainException($"Cannot start appointment with status {_status}");

        _status = "InProgress";
    }

    public void Complete()
    {
        if (_status != "InProgress")
            throw new DomainException($"Cannot complete appointment with status {_status}");

        _status = "Completed";
        AddDomainEvent(new AppointmentCompletedDomainEvent(this));
    }

    public void Cancel(string reason)
    {
        if (_status == "Completed")
            throw new DomainException("Cannot cancel completed appointment");

        _status = "Cancelled";
        AddDomainEvent(new AppointmentCancelledDomainEvent(this, reason));
    }
}
