using AdsManagerService.Domain.Events;
using AdsManagerService.Domain.Exceptions;
using AdsManagerService.Domain.SeedWork;

namespace AdsManagerService.Domain.AggregatesModel.CampaignAggregate;

/// <summary>
/// EN: Campaign aggregate root - the top level of the 3-tier ads structure.
/// VI: Campaign aggregate root - cấp trên nhất của cấu trúc ads 3 cấp.
/// </summary>
public class Campaign : Entity, IAggregateRoot
{
    // Private fields for encapsulation
    private string _name = null!;
    private string? _description;
    private Guid _advertiserId;
    private CampaignStatus _status = null!;
    private CampaignObjective _objective = null!;
    private CampaignBudget _budget = null!;
    private DateTime? _startDate;
    private DateTime? _endDate;
    private DateTime _createdAt;
    private DateTime? _updatedAt;

    /// <summary>
    /// EN: Campaign name.
    /// VI: Tên chiến dịch.
    /// </summary>
    public string Name => _name;

    /// <summary>
    /// EN: Campaign description.
    /// VI: Mô tả chiến dịch.
    /// </summary>
    public string? Description => _description;

    /// <summary>
    /// EN: Advertiser who owns this campaign.
    /// VI: Nhà quảng cáo sở hữu chiến dịch này.
    /// </summary>
    public Guid AdvertiserId => _advertiserId;

    /// <summary>
    /// EN: Current campaign status.
    /// VI: Trạng thái hiện tại của chiến dịch.
    /// </summary>
    public CampaignStatus Status => _status;
    public int StatusId { get; private set; }

    /// <summary>
    /// EN: Campaign objective (Awareness, Traffic, Conversion).
    /// VI: Mục tiêu chiến dịch (Nhận diện, Traffic, Chuyển đổi).
    /// </summary>
    public CampaignObjective Objective => _objective;
    public int ObjectiveId { get; private set; }

    /// <summary>
    /// EN: Campaign budget settings.
    /// VI: Cài đặt ngân sách chiến dịch.
    /// </summary>
    public CampaignBudget Budget => _budget;

    /// <summary>
    /// EN: Campaign start date.
    /// VI: Ngày bắt đầu chiến dịch.
    /// </summary>
    public DateTime? StartDate => _startDate;

    /// <summary>
    /// EN: Campaign end date.
    /// VI: Ngày kết thúc chiến dịch.
    /// </summary>
    public DateTime? EndDate => _endDate;

    /// <summary>
    /// EN: Creation timestamp.
    /// VI: Thời gian tạo.
    /// </summary>
    public DateTime CreatedAt => _createdAt;

    /// <summary>
    /// EN: Last update timestamp.
    /// VI: Thời gian cập nhật cuối.
    /// </summary>
    public DateTime? UpdatedAt => _updatedAt;

    /// <summary>
    /// EN: Total spend so far.
    /// VI: Tổng chi tiêu đến hiện tại.
    /// </summary>
    public decimal TotalSpend { get; private set; }

    // EF Core constructor
    protected Campaign() { }

    /// <summary>
    /// EN: Create a new campaign.
    /// VI: Tạo chiến dịch mới.
    /// </summary>
    public Campaign(
        Guid advertiserId,
        string name,
        CampaignObjective objective,
        CampaignBudget budget,
        string? description = null) : this()
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new AdsDomainException("Campaign name cannot be empty");

        Id = Guid.NewGuid();
        _advertiserId = advertiserId;
        _name = name;
        _description = description;
        _objective = objective;
        ObjectiveId = objective.Id;
        _budget = budget;
        _status = CampaignStatus.Draft;
        StatusId = CampaignStatus.Draft.Id;
        _createdAt = DateTime.UtcNow;
        TotalSpend = 0;

        AddDomainEvent(new CampaignCreatedDomainEvent(this));
    }

    /// <summary>
    /// EN: Update campaign information.
    /// VI: Cập nhật thông tin chiến dịch.
    /// </summary>
    public void Update(string name, string? description)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new AdsDomainException("Campaign name cannot be empty");

        if (_status == CampaignStatus.Archived)
            throw new AdsDomainException("Cannot update an archived campaign");

        _name = name;
        _description = description;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Set campaign schedule.
    /// VI: Đặt lịch chiến dịch.
    /// </summary>
    public void SetSchedule(DateTime startDate, DateTime? endDate = null)
    {
        if (endDate.HasValue && endDate.Value <= startDate)
            throw new AdsDomainException("End date must be after start date");

        _startDate = startDate;
        _endDate = endDate;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Update campaign budget.
    /// VI: Cập nhật ngân sách chiến dịch.
    /// </summary>
    public void SetBudget(CampaignBudget budget)
    {
        _budget = budget ?? throw new AdsDomainException("Budget cannot be null");
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Activate the campaign (start running ads).
    /// VI: Kích hoạt chiến dịch (bắt đầu chạy quảng cáo).
    /// </summary>
    public void Activate()
    {
        if (_status != CampaignStatus.Draft && _status != CampaignStatus.Paused)
            throw new AdsDomainException("Only draft or paused campaigns can be activated");

        var previousStatus = _status;
        _status = CampaignStatus.Active;
        StatusId = CampaignStatus.Active.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new CampaignActivatedDomainEvent(Id, previousStatus, _status));
    }

    /// <summary>
    /// EN: Pause the campaign.
    /// VI: Tạm dừng chiến dịch.
    /// </summary>
    public void Pause()
    {
        if (_status != CampaignStatus.Active)
            throw new AdsDomainException("Only active campaigns can be paused");

        var previousStatus = _status;
        _status = CampaignStatus.Paused;
        StatusId = CampaignStatus.Paused.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new CampaignStatusChangedDomainEvent(Id, previousStatus, _status));
    }

    /// <summary>
    /// EN: Complete the campaign.
    /// VI: Hoàn thành chiến dịch.
    /// </summary>
    public void Complete()
    {
        if (_status != CampaignStatus.Active)
            throw new AdsDomainException("Only active campaigns can be completed");

        var previousStatus = _status;
        _status = CampaignStatus.Completed;
        StatusId = CampaignStatus.Completed.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new CampaignStatusChangedDomainEvent(Id, previousStatus, _status));
    }

    /// <summary>
    /// EN: Archive the campaign.
    /// VI: Lưu trữ chiến dịch.
    /// </summary>
    public void Archive()
    {
        if (_status == CampaignStatus.Active)
            throw new AdsDomainException("Cannot archive an active campaign. Pause it first.");

        var previousStatus = _status;
        _status = CampaignStatus.Archived;
        StatusId = CampaignStatus.Archived.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new CampaignStatusChangedDomainEvent(Id, previousStatus, _status));
    }

    /// <summary>
    /// EN: Record spend for this campaign.
    /// VI: Ghi nhận chi tiêu cho chiến dịch này.
    /// </summary>
    public void RecordSpend(decimal amount)
    {
        if (amount < 0)
            throw new AdsDomainException("Spend amount cannot be negative");

        TotalSpend += amount;
        _updatedAt = DateTime.UtcNow;
    }
}
