package com.pablotj.ai.chat.domain.model;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * Aggregate root representing a conversation with its messages.
 * Encapsulates conversation business logic and invariants.
 */
public final class Conversation {

    private static final int MAX_MESSAGES_PER_CONVERSATION = 1000;

    private final ConversationId conversationId;
    private final ConversationSummary summary;
    private final List<ConversationMessage> messages;
    private final Instant createdAt;
    private final ConversationStatus status;

    private Conversation(Builder builder) {
        this.conversationId = Objects.requireNonNull(builder.conversationId, "Conversation ID is required");
        this.summary = Objects.requireNonNull(builder.summary, "Conversation summary is required");
        this.messages = new ArrayList<>(builder.messages);
        this.createdAt = Objects.requireNonNull(builder.createdAt, "Created timestamp is required");
        this.status = Objects.requireNonNull(builder.status, "Conversation status is required");

        validateInvariants();
    }

    public static Builder builder() {
        return new Builder();
    }

    public static Conversation createNew(ConversationSummary summary) {
        return builder()
                .conversationId(ConversationId.generate())
                .summary(summary)
                .messages(Collections.emptyList())
                .createdAt(Instant.now())
                .status(ConversationStatus.ACTIVE)
                .build();
    }

    private void validateInvariants() {
        if (messages.size() > MAX_MESSAGES_PER_CONVERSATION) {
            throw new IllegalStateException(
                    String.format("Conversation cannot have more than %d messages", MAX_MESSAGES_PER_CONVERSATION)
            );
        }
    }

    // Getters
    public ConversationId getConversationId() {
        return conversationId;
    }

    public ConversationSummary getSummary() {
        return summary;
    }

    public List<ConversationMessage> getMessages() {
        return Collections.unmodifiableList(messages);
    }

    public Instant getCreatedAt() {
        return createdAt;
    }

    public ConversationStatus getStatus() {
        return status;
    }

    // Domain behavior
    public Conversation addMessage(ConversationMessage message) {
        if (!message.getConversationId().equals(this.conversationId)) {
            throw new IllegalArgumentException("Message does not belong to this conversation");
        }

        List<ConversationMessage> newMessages = new ArrayList<>(this.messages);
        newMessages.add(message);

        return builder()
                .conversationId(this.conversationId)
                .summary(this.summary)
                .messages(newMessages)
                .createdAt(this.createdAt)
                .status(this.status)
                .build();
    }

    public Conversation updateSummary(ConversationSummary newSummary) {
        return builder()
                .conversationId(this.conversationId)
                .summary(newSummary)
                .messages(this.messages)
                .createdAt(this.createdAt)
                .status(this.status)
                .build();
    }

    public Conversation changeStatus(ConversationStatus newStatus) {
        return builder()
                .conversationId(this.conversationId)
                .summary(this.summary)
                .messages(this.messages)
                .createdAt(this.createdAt)
                .status(newStatus)
                .build();
    }

    public Optional<ConversationMessage> getLastMessage() {
        return messages.isEmpty() ?
                Optional.empty() :
                Optional.of(messages.get(messages.size() - 1));
    }

    public List<ConversationMessage> getMessagesByRole(MessageRole role) {
        return messages.stream()
                .filter(message -> message.getRole() == role)
                .toList();
    }

    public int getMessageCount() {
        return messages.size();
    }

    public boolean isEmpty() {
        return messages.isEmpty();
    }

    public boolean isActive() {
        return status == ConversationStatus.ACTIVE;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Conversation that = (Conversation) obj;
        return Objects.equals(conversationId, that.conversationId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(conversationId);
    }

    @Override
    public String toString() {
        return String.format("Conversation{id=%s, summary='%s', messages=%d, status=%s}",
                conversationId, summary, messages.size(), status);
    }

    public static final class Builder {
        private ConversationId conversationId;
        private ConversationSummary summary;
        private List<ConversationMessage> messages = new ArrayList<>();
        private Instant createdAt;
        private ConversationStatus status;

        private Builder() {
        }

        public Builder conversationId(ConversationId conversationId) {
            this.conversationId = conversationId;
            return this;
        }

        public Builder summary(ConversationSummary summary) {
            this.summary = summary;
            return this;
        }

        public Builder messages(List<ConversationMessage> messages) {
            this.messages = messages != null ? new ArrayList<>(messages) : new ArrayList<>();
            return this;
        }

        public Builder createdAt(Instant createdAt) {
            this.createdAt = createdAt;
            return this;
        }

        public Builder status(ConversationStatus status) {
            this.status = status;
            return this;
        }

        public Conversation build() {
            return new Conversation(this);
        }
    }
}
