using ColaFlow.Shared.Kernel.Common;
using ColaFlow.Modules.IssueManagement.Domain.Enums;
using ColaFlow.Modules.IssueManagement.Domain.Events;
using ColaFlow.Modules.IssueManagement.Domain.Exceptions;

namespace ColaFlow.Modules.IssueManagement.Domain.Entities;

/// <summary>
/// Issue aggregate root - represents a work item in the project management system
/// Supports Kanban board workflow and real-time collaboration
/// </summary>
public sealed class Issue : AggregateRoot
{
    public new Guid Id { get; private set; }
    public Guid ProjectId { get; private set; }
    public Guid TenantId { get; private set; }
    public string Title { get; private set; }
    public string Description { get; private set; }
    public IssueType Type { get; private set; }
    public IssueStatus Status { get; private set; }
    public IssuePriority Priority { get; private set; }
    public Guid? AssigneeId { get; private set; }
    public Guid ReporterId { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public DateTime? UpdatedAt { get; private set; }

    // EF Core constructor
    private Issue()
    {
        Title = null!;
        Description = null!;
    }

    /// <summary>
    /// Factory method to create a new Issue
    /// </summary>
    public static Issue Create(
        Guid projectId,
        Guid tenantId,
        string title,
        string description,
        IssueType type,
        IssuePriority priority,
        Guid reporterId)
    {
        if (string.IsNullOrWhiteSpace(title))
            throw new DomainException("Issue title cannot be empty");

        if (title.Length > 200)
            throw new DomainException("Issue title cannot exceed 200 characters");

        if (description != null && description.Length > 2000)
            throw new DomainException("Issue description cannot exceed 2000 characters");

        var issue = new Issue
        {
            Id = Guid.NewGuid(),
            ProjectId = projectId,
            TenantId = tenantId,
            Title = title,
            Description = description ?? string.Empty,
            Type = type,
            Status = IssueStatus.Backlog, // Default status for new issues
            Priority = priority,
            ReporterId = reporterId,
            CreatedAt = DateTime.UtcNow
        };

        issue.AddDomainEvent(new IssueCreatedEvent(
            issue.Id,
            issue.TenantId,
            issue.ProjectId,
            issue.Title
        ));

        return issue;
    }

    /// <summary>
    /// Update issue details
    /// </summary>
    public void Update(string title, string description, IssuePriority priority)
    {
        if (string.IsNullOrWhiteSpace(title))
            throw new DomainException("Issue title cannot be empty");

        if (title.Length > 200)
            throw new DomainException("Issue title cannot exceed 200 characters");

        if (description != null && description.Length > 2000)
            throw new DomainException("Issue description cannot exceed 2000 characters");

        Title = title;
        Description = description ?? string.Empty;
        Priority = priority;
        UpdatedAt = DateTime.UtcNow;

        AddDomainEvent(new IssueUpdatedEvent(Id, TenantId, ProjectId));
    }

    /// <summary>
    /// Change issue status (for Kanban board drag-and-drop)
    /// </summary>
    public void ChangeStatus(IssueStatus newStatus)
    {
        var oldStatus = Status;
        Status = newStatus;
        UpdatedAt = DateTime.UtcNow;

        AddDomainEvent(new IssueStatusChangedEvent(
            Id,
            TenantId,
            ProjectId,
            oldStatus,
            newStatus
        ));
    }

    /// <summary>
    /// Assign issue to a user (null to unassign)
    /// </summary>
    public void Assign(Guid? assigneeId)
    {
        AssigneeId = assigneeId;
        UpdatedAt = DateTime.UtcNow;

        AddDomainEvent(new IssueAssignedEvent(
            Id,
            TenantId,
            ProjectId,
            assigneeId
        ));
    }

    /// <summary>
    /// Mark issue as deleted (soft delete handled by event)
    /// </summary>
    public void Delete()
    {
        AddDomainEvent(new IssueDeletedEvent(Id, TenantId, ProjectId));
    }
}
