// 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>
/// <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!; // Pending, Confirmed, InProgress, Completed, Cancelled, NoShow
    private string? _notes;
    private DateTime _createdAt;

    /// <summary>
    /// EN: The shop this appointment belongs to.
    /// VI: Shop mà cuộc hẹn này thuộc về.
    /// </summary>
    public Guid ShopId => _shopId;

    /// <summary>
    /// EN: The customer who booked this appointment.
    /// VI: Khách hàng đã đặt cuộc hẹn này.
    /// </summary>
    public Guid? CustomerId => _customerId;

    /// <summary>
    /// EN: The staff/therapist assigned to this appointment.
    /// VI: Nhân viên/chuyên viên được gán cho cuộc hẹn này.
    /// </summary>
    public Guid? StaffId => _staffId;

    /// <summary>
    /// EN: The resource (room/bed) assigned to this appointment.
    /// VI: Tài nguyên (phòng/giường) được gán cho cuộc hẹn này.
    /// </summary>
    public Guid? ResourceId => _resourceId;

    /// <summary>
    /// EN: The service being booked.
    /// VI: Dịch vụ được đặt.
    /// </summary>
    public Guid ServiceId => _serviceId;

    /// <summary>
    /// EN: Appointment start time.
    /// VI: Thời gian bắt đầu cuộc hẹn.
    /// </summary>
    public DateTime StartTime => _startTime;

    /// <summary>
    /// EN: Appointment end time.
    /// VI: Thời gian kết thúc cuộc hẹn.
    /// </summary>
    public DateTime EndTime => _endTime;

    /// <summary>
    /// EN: Current status of the appointment.
    /// VI: Trạng thái hiện tại của cuộc hẹn.
    /// </summary>
    public string Status => _status;

    /// <summary>
    /// EN: Optional notes for the appointment.
    /// VI: Ghi chú tùy chọn cho cuộc hẹn.
    /// </summary>
    public string? Notes => _notes;

    /// <summary>
    /// EN: When the appointment was created.
    /// VI: Thời điểm tạo cuộc hẹn.
    /// </summary>
    public DateTime CreatedAt => _createdAt;

    /// <summary>
    /// EN: EF Core parameterless constructor.
    /// VI: Constructor không tham số cho EF Core.
    /// </summary>
    protected Appointment()
    {
    }

    /// <summary>
    /// EN: Create a new appointment.
    /// VI: Tạo cuộc hẹn mới.
    /// </summary>
    public Appointment(
        Guid shopId,
        Guid serviceId,
        DateTime startTime,
        DateTime endTime,
        Guid? customerId = null,
        Guid? staffId = null,
        Guid? resourceId = null,
        string? notes = null)
    {
        if (shopId == Guid.Empty)
            throw new DomainException("Shop ID cannot be empty / Shop ID không được trống");
        if (serviceId == Guid.Empty)
            throw new DomainException("Service ID cannot be empty / Service ID không được trống");
        if (endTime <= startTime)
            throw new DomainException("End time must be after start time / Thời gian kết thúc phải sau thời gian bắt đầu");

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

        AddDomainEvent(new AppointmentCreatedDomainEvent(this));
    }

    /// <summary>
    /// EN: Confirm the appointment.
    /// VI: Xác nhận cuộc hẹn.
    /// </summary>
    public void Confirm()
    {
        if (_status != "Pending")
            throw new DomainException($"Cannot confirm appointment with status {_status} / Không thể xác nhận cuộc hẹn có trạng thái {_status}");

        _status = "Confirmed";
        AddDomainEvent(new AppointmentConfirmedDomainEvent(this));
    }

    /// <summary>
    /// EN: Start the appointment (mark as in progress).
    /// VI: Bắt đầu cuộc hẹn (đánh dấu đang thực hiện).
    /// </summary>
    public void MarkAsInProgress()
    {
        if (_status != "Confirmed")
            throw new DomainException($"Cannot start appointment with status {_status} / Không thể bắt đầu cuộc hẹn có trạng thái {_status}");

        _status = "InProgress";
    }

    /// <summary>
    /// EN: Complete the appointment.
    /// VI: Hoàn thành cuộc hẹn.
    /// </summary>
    public void Complete()
    {
        if (_status != "InProgress")
            throw new DomainException($"Cannot complete appointment with status {_status} / Không thể hoàn thành cuộc hẹn có trạng thái {_status}");

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

    /// <summary>
    /// EN: Cancel the appointment with a reason.
    /// VI: Hủy cuộc hẹn với lý do.
    /// </summary>
    /// <param name="reason">EN: Cancellation reason / VI: Lý do hủy</param>
    public void Cancel(string reason)
    {
        if (_status == "Completed")
            throw new DomainException("Cannot cancel completed appointment / Không thể hủy cuộc hẹn đã hoàn thành");
        if (_status == "Cancelled")
            throw new DomainException("Appointment is already cancelled / Cuộc hẹn đã bị hủy rồi");

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

    /// <summary>
    /// EN: Mark the appointment as no-show (customer didn't arrive).
    /// VI: Đánh dấu cuộc hẹn là không đến (khách không đến).
    /// </summary>
    public void MarkNoShow()
    {
        if (_status != "Confirmed" && _status != "Pending")
            throw new DomainException($"Cannot mark no-show for appointment with status {_status} / Không thể đánh dấu không đến cho cuộc hẹn có trạng thái {_status}");

        _status = "NoShow";
    }
}
