using FacebookService.Domain.Events;
using FacebookService.Domain.Exceptions;
using FacebookService.Domain.SeedWork;

namespace FacebookService.Domain.AggregatesModel.ChatbotAggregate;

/// <summary>
/// EN: ChatbotFlow aggregate root defining automation rules and conversation flows.
/// VI: ChatbotFlow aggregate root định nghĩa rules automation và luồng hội thoại.
/// </summary>
public class ChatbotFlow : Entity, IAggregateRoot
{
    // EN: Private fields for encapsulation
    // VI: Fields private để đóng gói
    private string _name = null!;
    private Guid _shopId;
    private FlowTriggerType _triggerType = null!;
    private string _triggerValue = null!;
    private bool _isActive;
    private DateTime _createdAt;
    private DateTime? _updatedAt;
    private List<FlowNode> _nodes = new();

    /// <summary>
    /// EN: Flow name. / VI: Tên flow.
    /// </summary>
    public string Name => _name;

    /// <summary>
    /// EN: Shop ID that owns this flow.
    /// VI: ID shop sở hữu flow này.
    /// </summary>
    public Guid ShopId => _shopId;

    /// <summary>
    /// EN: Trigger type. / VI: Loại trigger.
    /// </summary>
    public FlowTriggerType TriggerType => _triggerType;

    /// <summary>
    /// EN: Trigger type ID for EF Core. / VI: ID loại trigger cho EF Core.
    /// </summary>
    public int TriggerTypeId { get; private set; }

    /// <summary>
    /// EN: Trigger value (keyword pattern, postback payload, etc.).
    /// VI: Giá trị trigger (pattern keyword, postback payload, v.v.).
    /// </summary>
    public string TriggerValue => _triggerValue;

    /// <summary>
    /// EN: Whether the flow is active. / VI: Flow có đang hoạt động không.
    /// </summary>
    public bool IsActive => _isActive;

    /// <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: All nodes in this flow.
    /// VI: Tất cả nodes trong flow này.
    /// </summary>
    public IReadOnlyCollection<FlowNode> Nodes => _nodes.AsReadOnly();

    /// <summary>
    /// EN: Private constructor for EF Core. / VI: Constructor private cho EF Core.
    /// </summary>
    protected ChatbotFlow()
    {
    }

    /// <summary>
    /// EN: Create a new chatbot flow.
    /// VI: Tạo chatbot flow mới.
    /// </summary>
    public ChatbotFlow(
        Guid shopId,
        string name,
        FlowTriggerType triggerType,
        string triggerValue) : this()
    {
        if (shopId == Guid.Empty)
            throw new FacebookDomainException("Shop ID cannot be empty");
        
        if (string.IsNullOrWhiteSpace(name))
            throw new FacebookDomainException("Flow name cannot be empty");
        
        if (string.IsNullOrWhiteSpace(triggerValue))
            throw new FacebookDomainException("Trigger value cannot be empty");

        Id = Guid.NewGuid();
        _shopId = shopId;
        _name = name;
        _triggerType = triggerType;
        TriggerTypeId = triggerType.Id;
        _triggerValue = triggerValue;
        _isActive = false;
        _createdAt = DateTime.UtcNow;

        AddDomainEvent(new ChatbotFlowCreatedDomainEvent(this));
    }

    /// <summary>
    /// EN: Add a node to the flow.
    /// VI: Thêm node vào flow.
    /// </summary>
    public void AddNode(FlowNode node)
    {
        if (node == null)
            throw new FacebookDomainException("Node cannot be null");

        _nodes.Add(node);
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Remove a node from the flow.
    /// VI: Xóa node khỏi flow.
    /// </summary>
    public void RemoveNode(Guid nodeId)
    {
        var node = _nodes.FirstOrDefault(n => n.Id == nodeId);
        if (node != null)
        {
            _nodes.Remove(node);
            _updatedAt = DateTime.UtcNow;
        }
    }

    /// <summary>
    /// EN: Connect two nodes in the flow.
    /// VI: Kết nối hai nodes trong flow.
    /// </summary>
    public void ConnectNodes(Guid fromNodeId, Guid toNodeId)
    {
        var fromNode = _nodes.FirstOrDefault(n => n.Id == fromNodeId);
        var toNode = _nodes.FirstOrDefault(n => n.Id == toNodeId);

        if (fromNode == null)
            throw new FacebookDomainException($"Source node {fromNodeId} not found");
        
        if (toNode == null)
            throw new FacebookDomainException($"Target node {toNodeId} not found");

        fromNode.ConnectTo(toNodeId);
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Update flow information.
    /// VI: Cập nhật thông tin flow.
    /// </summary>
    public void Update(string name, string triggerValue)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new FacebookDomainException("Flow name cannot be empty");
        
        if (string.IsNullOrWhiteSpace(triggerValue))
            throw new FacebookDomainException("Trigger value cannot be empty");

        _name = name;
        _triggerValue = triggerValue;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Activate the flow.
    /// VI: Kích hoạt flow.
    /// </summary>
    public void Activate()
    {
        ValidateFlowStructure();
        _isActive = true;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Deactivate the flow.
    /// VI: Vô hiệu hóa flow.
    /// </summary>
    public void Deactivate()
    {
        _isActive = false;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Validate flow structure before activation.
    /// VI: Validate cấu trúc flow trước khi kích hoạt.
    /// </summary>
    private void ValidateFlowStructure()
    {
        if (_nodes.Count == 0)
            throw new FacebookDomainException("Flow must have at least one node");

        var startNodes = _nodes.Where(n => n.NodeType == NodeType.Start).ToList();
        if (startNodes.Count == 0)
            throw new FacebookDomainException("Flow must have exactly one Start node");
        
        if (startNodes.Count > 1)
            throw new FacebookDomainException("Flow cannot have more than one Start node");

        var endNodes = _nodes.Where(n => n.NodeType == NodeType.End).ToList();
        if (endNodes.Count == 0)
            throw new FacebookDomainException("Flow must have at least one End node");

        // EN: Check for unreachable nodes (simple check)
        // VI: Kiểm tra nodes không thể truy cập (kiểm tra đơn giản)
        var startNode = startNodes.First();
        var reachable = new HashSet<Guid> { startNode.Id };
        var queue = new Queue<Guid>();
        
        foreach (var nextId in startNode.NextNodeIds)
        {
            queue.Enqueue(nextId);
        }

        while (queue.Count > 0)
        {
            var currentId = queue.Dequeue();
            if (reachable.Contains(currentId))
                continue;

            reachable.Add(currentId);
            var currentNode = _nodes.FirstOrDefault(n => n.Id == currentId);
            if (currentNode != null)
            {
                foreach (var nextId in currentNode.NextNodeIds)
                {
                    if (!reachable.Contains(nextId))
                        queue.Enqueue(nextId);
                }
            }
        }

        var unreachableNodes = _nodes.Where(n => !reachable.Contains(n.Id) && n.NodeType != NodeType.Start).ToList();
        if (unreachableNodes.Any())
        {
            throw new FacebookDomainException($"Flow has unreachable nodes: {string.Join(", ", unreachableNodes.Select(n => n.Id))}");
        }
    }

    /// <summary>
    /// EN: Get the start node of the flow.
    /// VI: Lấy node bắt đầu của flow.
    /// </summary>
    public FlowNode? GetStartNode()
    {
        return _nodes.FirstOrDefault(n => n.NodeType == NodeType.Start);
    }
}
