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

#pragma warning disable CS0649 // Field is never assigned to (EF Core managed)

namespace AdsManagerService.Domain.AggregatesModel.AdAggregate;

/// <summary>
/// EN: Ad aggregate root - the creative and content for an advertisement.
/// VI: Ad aggregate root - creative và nội dung cho một quảng cáo.
/// </summary>
public class Ad : Entity, IAggregateRoot
{
    private string _name = null!;
    private Guid _adSetId;
    private AdFormat _format = null!;
    private AdStatus _status = null!;
    private AdReviewStatus _reviewStatus = null!;
    private string? _headline;
    private string? _primaryText;
    private string? _description;
    private string? _callToAction;
    private string? _destinationUrl;
    private string? _creativeUrl;
    private DateTime _createdAt;
    private DateTime? _updatedAt;

    public string Name => _name;
    public Guid AdSetId => _adSetId;
    public AdFormat Format => _format;
    public int FormatId { get; private set; }
    public AdStatus Status => _status;
    public int StatusId { get; private set; }
    public AdReviewStatus ReviewStatus => _reviewStatus;
    public int ReviewStatusId { get; private set; }
    public string? Headline => _headline;
    public string? PrimaryText => _primaryText;
    public string? Description => _description;
    public string? CallToAction => _callToAction;
    public string? DestinationUrl => _destinationUrl;
    public string? CreativeUrl => _creativeUrl;
    public DateTime CreatedAt => _createdAt;
    public DateTime? UpdatedAt => _updatedAt;

    protected Ad() { }

    public Ad(
        Guid adSetId,
        string name,
        AdFormat format,
        string? headline = null,
        string? primaryText = null,
        string? callToAction = null,
        string? destinationUrl = null,
        string? creativeUrl = null) : this()
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new AdsDomainException("Ad name cannot be empty");

        Id = Guid.NewGuid();
        _adSetId = adSetId;
        _name = name;
        _format = format;
        FormatId = format.Id;
        _headline = headline;
        _primaryText = primaryText;
        _callToAction = callToAction;
        _destinationUrl = destinationUrl;
        _creativeUrl = creativeUrl;
        _status = AdStatus.Draft;
        StatusId = AdStatus.Draft.Id;
        _reviewStatus = AdReviewStatus.NotSubmitted;
        ReviewStatusId = AdReviewStatus.NotSubmitted.Id;
        _createdAt = DateTime.UtcNow;
    }

    public void Update(string name, string? headline, string? primaryText, string? callToAction, string? destinationUrl)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new AdsDomainException("Ad name cannot be empty");

        _name = name;
        _headline = headline;
        _primaryText = primaryText;
        _callToAction = callToAction;
        _destinationUrl = destinationUrl;
        _updatedAt = DateTime.UtcNow;

        // Reset review if content changed
        if (_reviewStatus == AdReviewStatus.Approved)
        {
            _reviewStatus = AdReviewStatus.PendingReview;
            ReviewStatusId = AdReviewStatus.PendingReview.Id;
        }
    }

    public void SetCreative(string creativeUrl)
    {
        _creativeUrl = creativeUrl;
        _updatedAt = DateTime.UtcNow;
    }

    public void SubmitForReview()
    {
        if (_reviewStatus != AdReviewStatus.NotSubmitted && _reviewStatus != AdReviewStatus.Rejected)
            throw new AdsDomainException("Ad is already submitted or approved");

        _reviewStatus = AdReviewStatus.PendingReview;
        ReviewStatusId = AdReviewStatus.PendingReview.Id;
        _updatedAt = DateTime.UtcNow;
    }

    public void Approve()
    {
        if (_reviewStatus != AdReviewStatus.PendingReview)
            throw new AdsDomainException("Only pending ads can be approved");

        _reviewStatus = AdReviewStatus.Approved;
        ReviewStatusId = AdReviewStatus.Approved.Id;
        _updatedAt = DateTime.UtcNow;
    }

    public void Reject(string reason)
    {
        if (_reviewStatus != AdReviewStatus.PendingReview)
            throw new AdsDomainException("Only pending ads can be rejected");

        _reviewStatus = AdReviewStatus.Rejected;
        ReviewStatusId = AdReviewStatus.Rejected.Id;
        _updatedAt = DateTime.UtcNow;
    }

    public void Activate()
    {
        if (_reviewStatus != AdReviewStatus.Approved)
            throw new AdsDomainException("Only approved ads can be activated");

        _status = AdStatus.Active;
        StatusId = AdStatus.Active.Id;
        _updatedAt = DateTime.UtcNow;
    }

    public void Pause()
    {
        if (_status != AdStatus.Active)
            throw new AdsDomainException("Only active ads can be paused");

        _status = AdStatus.Paused;
        StatusId = AdStatus.Paused.Id;
        _updatedAt = DateTime.UtcNow;
    }
}

public class AdFormat : Enumeration
{
    public static readonly AdFormat SingleImage = new(1, "single_image");
    public static readonly AdFormat SingleVideo = new(2, "single_video");
    public static readonly AdFormat Carousel = new(3, "carousel");
    public static readonly AdFormat Collection = new(4, "collection");
    public static readonly AdFormat Stories = new(5, "stories");

    public AdFormat(int id, string name) : base(id, name) { }
    public static IEnumerable<AdFormat> List() => new[] { SingleImage, SingleVideo, Carousel, Collection, Stories };
}

public class AdStatus : Enumeration
{
    public static readonly AdStatus Draft = new(1, nameof(Draft).ToLowerInvariant());
    public static readonly AdStatus Active = new(2, nameof(Active).ToLowerInvariant());
    public static readonly AdStatus Paused = new(3, nameof(Paused).ToLowerInvariant());
    public static readonly AdStatus Archived = new(4, nameof(Archived).ToLowerInvariant());

    public AdStatus(int id, string name) : base(id, name) { }
    public static IEnumerable<AdStatus> List() => new[] { Draft, Active, Paused, Archived };
}

public class AdReviewStatus : Enumeration
{
    public static readonly AdReviewStatus NotSubmitted = new(1, "not_submitted");
    public static readonly AdReviewStatus PendingReview = new(2, "pending_review");
    public static readonly AdReviewStatus Approved = new(3, nameof(Approved).ToLowerInvariant());
    public static readonly AdReviewStatus Rejected = new(4, nameof(Rejected).ToLowerInvariant());

    public AdReviewStatus(int id, string name) : base(id, name) { }
    public static IEnumerable<AdReviewStatus> List() => new[] { NotSubmitted, PendingReview, Approved, Rejected };
}
