package handlers

import (
	"errors"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/orchard9/rdev/internal/auth"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/service"
	"github.com/orchard9/rdev/internal/validate"
	"github.com/orchard9/rdev/pkg/api"
)

// QuestionsHandler handles question endpoints.
type QuestionsHandler struct {
	questionService *service.QuestionService
}

// NewQuestionsHandler creates a new questions handler.
func NewQuestionsHandler(questionService *service.QuestionService) *QuestionsHandler {
	return &QuestionsHandler{
		questionService: questionService,
	}
}

// Mount registers question routes.
func (h *QuestionsHandler) Mount(r api.Router) {
	r.Route("/projects/{id}/questions", func(r chi.Router) {
		// Read operations
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).
			Get("/", h.ListUnansweredQuestions)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).
			Get("/{questionId}", h.GetQuestion)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).
			Get("/conversation/{conversationId}", h.ListQuestionsByConversation)

		// Write operations
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).
			Post("/", h.CreateQuestion)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).
			Post("/{questionId}/answer", h.AnswerQuestion)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).
			Delete("/{questionId}", h.DeleteQuestion)
	})
}

// CreateQuestionRequest is the request body for POST /projects/{id}/questions.
type CreateQuestionRequest struct {
	ConversationID string            `json:"conversation_id"`
	Type           string            `json:"type"`
	Text           string            `json:"text"`
	Choices        []string          `json:"choices,omitempty"`
	Metadata       map[string]string `json:"metadata,omitempty"`
}

// CreateQuestion creates a new question.
// POST /projects/{id}/questions
func (h *QuestionsHandler) CreateQuestion(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")

	var req CreateQuestionRequest
	if err := api.DecodeJSON(r, &req); err != nil {
		api.WriteBadRequest(w, r, "invalid request body")
		return
	}

	v := validate.New()
	v.Required(req.ConversationID, "conversation_id")
	v.Required(req.Type, "type")
	v.Required(req.Text, "text")
	if err := v.Error(); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	questionType := domain.QuestionType(req.Type)
	if questionType != domain.QuestionTypeText && questionType != domain.QuestionTypeChoice &&
		questionType != domain.QuestionTypeMultiChoice && questionType != domain.QuestionTypeYesNo {
		api.WriteBadRequest(w, r, "type must be 'text', 'choice', 'multichoice', or 'yesno'")
		return
	}

	question, err := h.questionService.CreateQuestion(
		r.Context(),
		domain.ConversationID(req.ConversationID),
		projectID,
		questionType,
		req.Text,
		req.Choices,
		req.Metadata,
	)
	if err != nil {
		api.WriteInternalError(w, r, "failed to create question")
		return
	}

	api.WriteCreated(w, r, toQuestionDTO(question))
}

// ListUnansweredQuestions returns all unanswered questions for a project.
// GET /projects/{id}/questions
func (h *QuestionsHandler) ListUnansweredQuestions(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")

	questions, err := h.questionService.ListUnansweredQuestions(r.Context(), projectID)
	if err != nil {
		api.WriteInternalError(w, r, "failed to list questions")
		return
	}

	dtos := make([]*QuestionDTO, len(questions))
	for i, q := range questions {
		dtos[i] = toQuestionDTO(q)
	}

	api.WriteSuccess(w, r, map[string]any{
		"questions": dtos,
		"total":     len(dtos),
	})
}

// GetQuestion retrieves a question by ID.
// GET /projects/{id}/questions/{questionId}
func (h *QuestionsHandler) GetQuestion(w http.ResponseWriter, r *http.Request) {
	questionID := domain.QuestionID(chi.URLParam(r, "questionId"))

	question, err := h.questionService.GetQuestion(r.Context(), questionID)
	if err != nil {
		if errors.Is(err, domain.ErrQuestionNotFound) {
			api.WriteNotFound(w, r, "question not found")
			return
		}
		api.WriteInternalError(w, r, "failed to get question")
		return
	}

	api.WriteSuccess(w, r, toQuestionDTO(question))
}

// ListQuestionsByConversation returns all questions for a conversation.
// GET /projects/{id}/questions/conversation/{conversationId}
func (h *QuestionsHandler) ListQuestionsByConversation(w http.ResponseWriter, r *http.Request) {
	conversationID := domain.ConversationID(chi.URLParam(r, "conversationId"))

	questions, err := h.questionService.ListQuestionsByConversation(r.Context(), conversationID)
	if err != nil {
		api.WriteInternalError(w, r, "failed to list questions")
		return
	}

	dtos := make([]*QuestionDTO, len(questions))
	for i, q := range questions {
		dtos[i] = toQuestionDTO(q)
	}

	api.WriteSuccess(w, r, map[string]any{
		"questions": dtos,
		"total":     len(dtos),
	})
}

// AnswerQuestionRequest is the request body for POST /projects/{id}/questions/{questionId}/answer.
type AnswerQuestionRequest struct {
	Answer        *string  `json:"answer,omitempty"`
	AnswerChoices []string `json:"answer_choices,omitempty"`
}

// AnswerQuestion records an answer to a question.
// POST /projects/{id}/questions/{questionId}/answer
func (h *QuestionsHandler) AnswerQuestion(w http.ResponseWriter, r *http.Request) {
	questionID := domain.QuestionID(chi.URLParam(r, "questionId"))

	var req AnswerQuestionRequest
	if err := api.DecodeJSON(r, &req); err != nil {
		api.WriteBadRequest(w, r, "invalid request body")
		return
	}

	if err := h.questionService.AnswerQuestion(r.Context(), questionID, req.Answer, req.AnswerChoices); err != nil {
		if errors.Is(err, domain.ErrQuestionNotFound) {
			api.WriteNotFound(w, r, "question not found")
			return
		}
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	api.WriteSuccess(w, r, map[string]any{
		"message": "question answered",
	})
}

// DeleteQuestion deletes a question.
// DELETE /projects/{id}/questions/{questionId}
func (h *QuestionsHandler) DeleteQuestion(w http.ResponseWriter, r *http.Request) {
	questionID := domain.QuestionID(chi.URLParam(r, "questionId"))

	if err := h.questionService.DeleteQuestion(r.Context(), questionID); err != nil {
		if errors.Is(err, domain.ErrQuestionNotFound) {
			api.WriteNotFound(w, r, "question not found")
			return
		}
		api.WriteInternalError(w, r, "failed to delete question")
		return
	}

	api.WriteSuccess(w, r, map[string]any{
		"message": "question deleted",
	})
}
