package service

import (
	"context"
	"fmt"
	"time"

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

// QuestionService orchestrates question operations.
type QuestionService struct {
	repo port.QuestionRepository
}

// NewQuestionService creates a new question service.
func NewQuestionService(repo port.QuestionRepository) *QuestionService {
	return &QuestionService{repo: repo}
}

// CreateQuestion creates a new question.
func (s *QuestionService) CreateQuestion(ctx context.Context, conversationID domain.ConversationID, projectID string, questionType domain.QuestionType, text string, choices []string, metadata map[string]string) (*domain.Question, error) {
	if projectID == "" {
		return nil, fmt.Errorf("project_id is required")
	}
	if text == "" {
		return nil, fmt.Errorf("text is required")
	}

	// Validate choices for choice-based questions
	if (questionType == domain.QuestionTypeChoice || questionType == domain.QuestionTypeMultiChoice) && len(choices) == 0 {
		return nil, fmt.Errorf("choices are required for choice-based questions")
	}

	if metadata == nil {
		metadata = make(map[string]string)
	}

	question := &domain.Question{
		ID:             domain.QuestionID(uuid.New().String()),
		ConversationID: conversationID,
		ProjectID:      projectID,
		Type:           questionType,
		Text:           text,
		Choices:        choices,
		Metadata:       metadata,
	}

	startTime := time.Now()
	if err := s.repo.CreateQuestion(ctx, question); err != nil {
		return nil, err
	}

	log := logging.FromContext(ctx)
	log.Info("question created",
		"question_id", question.ID,
		logging.FieldProjectID, projectID,
		logging.FieldOperation, "create_question",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
		"question_type", questionType,
		"conversation_id", conversationID,
		"choice_count", len(choices),
	)

	return question, nil
}

// GetQuestion retrieves a question by ID.
func (s *QuestionService) GetQuestion(ctx context.Context, id domain.QuestionID) (*domain.Question, error) {
	return s.repo.GetQuestion(ctx, id)
}

// ListUnansweredQuestions returns all unanswered questions for a project.
func (s *QuestionService) ListUnansweredQuestions(ctx context.Context, projectID string) ([]*domain.Question, error) {
	return s.repo.ListUnansweredQuestions(ctx, projectID)
}

// ListQuestionsByConversation returns all questions for a conversation.
func (s *QuestionService) ListQuestionsByConversation(ctx context.Context, conversationID domain.ConversationID) ([]*domain.Question, error) {
	return s.repo.ListQuestionsByConversation(ctx, conversationID)
}

// AnswerQuestion records an answer to a question.
func (s *QuestionService) AnswerQuestion(ctx context.Context, id domain.QuestionID, answer *string, answerChoices []string) error {
	// Get question to validate answer type
	question, err := s.repo.GetQuestion(ctx, id)
	if err != nil {
		return err
	}

	// Validate answer based on question type
	switch question.Type {
	case domain.QuestionTypeText:
		if answer == nil || *answer == "" {
			return fmt.Errorf("text answer is required")
		}
	case domain.QuestionTypeYesNo:
		if answer == nil || (*answer != "yes" && *answer != "no") {
			return fmt.Errorf("answer must be 'yes' or 'no'")
		}
	case domain.QuestionTypeChoice:
		if answer == nil || *answer == "" {
			return fmt.Errorf("choice answer is required")
		}
		// Validate choice is in the available choices
		valid := false
		for _, choice := range question.Choices {
			if *answer == choice {
				valid = true
				break
			}
		}
		if !valid {
			return fmt.Errorf("answer must be one of the available choices")
		}
	case domain.QuestionTypeMultiChoice:
		if len(answerChoices) == 0 {
			return fmt.Errorf("at least one choice must be selected")
		}
		// Validate all choices are in the available choices
		for _, selected := range answerChoices {
			valid := false
			for _, choice := range question.Choices {
				if selected == choice {
					valid = true
					break
				}
			}
			if !valid {
				return fmt.Errorf("answer '%s' is not one of the available choices", selected)
			}
		}
	}

	startTime := time.Now()
	if err := s.repo.AnswerQuestion(ctx, id, answer, answerChoices); err != nil {
		return err
	}

	log := logging.FromContext(ctx)
	log.Info("question answered",
		"question_id", id,
		logging.FieldProjectID, question.ProjectID,
		logging.FieldOperation, "answer_question",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
		"question_type", question.Type,
		"answer_choice_count", len(answerChoices),
	)

	return nil
}

// DeleteQuestion deletes a question.
func (s *QuestionService) DeleteQuestion(ctx context.Context, id domain.QuestionID) error {
	startTime := time.Now()
	if err := s.repo.DeleteQuestion(ctx, id); err != nil {
		return err
	}

	log := logging.FromContext(ctx)
	log.Info("question deleted",
		"question_id", id,
		logging.FieldOperation, "delete_question",
		logging.FieldDuration, time.Since(startTime).Milliseconds(),
	)
	return nil
}
