package service

import (
	"context"
	"fmt"
	"time"

	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/logging"
	"github.com/orchard9/rdev/internal/port"
)

// ConversationService orchestrates conversation operations.
type ConversationService struct {
	repo port.ConversationRepository
}

// NewConversationService creates a new conversation service.
func NewConversationService(repo port.ConversationRepository) *ConversationService {
	return &ConversationService{repo: repo}
}

// CreateConversation creates a new conversation.
func (s *ConversationService) CreateConversation(ctx context.Context, projectID, title string) (*domain.Conversation, error) {
	if projectID == "" {
		return nil, fmt.Errorf("project_id is required")
	}
	if title == "" {
		title = "New Conversation"
	}

	startTime := time.Now()
	conv, err := s.repo.CreateConversation(ctx, projectID, title)
	if err != nil {
		return nil, err
	}

	log := logging.FromContext(ctx)
	log.Info("conversation created",
		"conversation_id", conv.ID,
		logging.FieldProjectID, projectID,
		logging.FieldOperation, "create_conversation",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
		"title", title,
	)

	return conv, nil
}

// GetConversation retrieves a conversation by ID.
func (s *ConversationService) GetConversation(ctx context.Context, id domain.ConversationID) (*domain.Conversation, error) {
	return s.repo.GetConversation(ctx, id)
}

// ListConversations returns all conversations for a project.
func (s *ConversationService) ListConversations(ctx context.Context, projectID string) ([]*domain.Conversation, error) {
	return s.repo.ListConversations(ctx, projectID)
}

// UpdateTitle updates the conversation title.
func (s *ConversationService) UpdateTitle(ctx context.Context, id domain.ConversationID, title string) error {
	if title == "" {
		return fmt.Errorf("title is required")
	}
	return s.repo.UpdateConversationTitle(ctx, id, title)
}

// DeleteConversation deletes a conversation and all its messages.
func (s *ConversationService) DeleteConversation(ctx context.Context, id domain.ConversationID) error {
	startTime := time.Now()
	if err := s.repo.DeleteConversation(ctx, id); err != nil {
		return err
	}

	log := logging.FromContext(ctx)
	log.Info("conversation deleted",
		"conversation_id", id,
		logging.FieldOperation, "delete_conversation",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
	)
	return nil
}

// AddMessage adds a message to a conversation.
func (s *ConversationService) AddMessage(ctx context.Context, conversationID domain.ConversationID, role domain.MessageRole, content string) (*domain.Message, error) {
	if content == "" {
		return nil, fmt.Errorf("message content is required")
	}

	startTime := time.Now()
	msg, err := s.repo.AddMessage(ctx, conversationID, role, content)
	if err != nil {
		return nil, err
	}

	log := logging.FromContext(ctx)
	log.Info("message added",
		"conversation_id", conversationID,
		"message_id", msg.ID,
		"role", role,
		logging.FieldOperation, "add_message",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
		"content_length", len(content),
	)

	return msg, nil
}

// GetMessages retrieves all messages for a conversation.
func (s *ConversationService) GetMessages(ctx context.Context, conversationID domain.ConversationID) ([]*domain.Message, error) {
	return s.repo.GetMessages(ctx, conversationID)
}

// GetConversationWithMessages retrieves a conversation with all messages.
func (s *ConversationService) GetConversationWithMessages(ctx context.Context, id domain.ConversationID) (*domain.ConversationWithMessages, error) {
	return s.repo.GetConversationWithMessages(ctx, id)
}
