package knowledgeqa

import (
	"context"
	"errors"
	"net/http"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
)

func (handler *turnHandler) CreateTurn(
	request api.WorkspaceScopedJSON[dto.KnowledgeQACreateTurnRequest],
) (api.Response, error) {
	sessionID := routeParameter(request.Gin, "sessionId")
	if sessionID == "" || trimmed(request.Body.UserMessageID) == "" ||
		trimmed(request.Body.Question) == "" {
		return api.Response{}, invalidRequest("sessionId, userMessageId, and question are required")
	}
	idempotencyKey, err := requireIdempotencyKey(request.Gin)
	if err != nil {
		return api.Response{}, err
	}
	session, err := handler.deps.Sessions.LoadSessionForOwner(
		request.Context(), request.WorkspaceID(), request.UserID(), sessionID,
	)
	if err != nil {
		return storeFailure(err)
	}
	if _, _, err := handler.resolveSubmissionPolicy(
		request.Context(), request.UserID(), request.WorkspaceID(),
		session.RequestedModel, session.RequestedReasonLevel,
	); err != nil {
		return api.Response{}, err
	}
	turn, err := handler.deps.Sessions.CreateTurn(
		context.WithoutCancel(request.Context()),
		CreateTurnParams{
			WorkspaceID:               session.WorkspaceID,
			OrganizationID:            session.OrganizationID,
			SessionID:                 session.ID,
			RequesterUserID:           session.OwnerUserID,
			UserMessageID:             request.Body.UserMessageID,
			Question:                  request.Body.Question,
			IdempotencyKey:            idempotencyKey,
			RequestedKnowledgeBaseIDs: optionalScope(request.Body.KnowledgeBaseIDs),
			RequestedSourceIDs:        optionalScope(request.Body.SourceIDs),
			Now:                       time.Now().UTC(),
		},
	)
	if err != nil {
		return storeFailure(err)
	}
	handler.notifyTurnUpdated(
		request.Context(), session.WorkspaceID, session.OwnerMemberID, session.ThreadChannelID, turn.ID,
	)
	handler.notifyQueued(turn)
	return api.Response{
		Status: http.StatusAccepted,
		Body: dto.KnowledgeQASessionResponse{
			Session: toSessionDTO(session),
			Turn:    toTurnDTO(turn),
		},
	}, nil
}

func (handler *turnHandler) RetryTurn(request api.WorkspaceScopedRequest) (api.Response, error) {
	sessionID := routeParameter(request.Gin, "sessionId")
	turnID := routeParameter(request.Gin, "turnId")
	if sessionID == "" || turnID == "" {
		return api.Response{}, invalidRequest("sessionId and turnId are required")
	}
	idempotencyKey, err := requireIdempotencyKey(request.Gin)
	if err != nil {
		return api.Response{}, err
	}
	session, err := handler.deps.Sessions.LoadSessionForOwner(
		request.Context(), request.WorkspaceID(), request.UserID(), sessionID,
	)
	if err != nil {
		return storeFailure(err)
	}
	if _, _, err := handler.resolveSubmissionPolicy(
		request.Context(), request.UserID(), request.WorkspaceID(),
		session.RequestedModel, session.RequestedReasonLevel,
	); err != nil {
		return api.Response{}, err
	}
	turn, err := handler.deps.Sessions.RetryTurn(
		context.WithoutCancel(request.Context()),
		RetryTurnParams{
			WorkspaceID:     session.WorkspaceID,
			SessionID:       session.ID,
			TurnID:          turnID,
			RequesterUserID: session.OwnerUserID,
			IdempotencyKey:  idempotencyKey,
			Now:             time.Now().UTC(),
		},
	)
	if err != nil {
		return storeFailure(err)
	}
	handler.notifyTurnUpdated(
		request.Context(), session.WorkspaceID, session.OwnerMemberID, session.ThreadChannelID, turn.ID,
	)
	handler.notifyQueued(turn)
	return api.Response{
		Status: http.StatusAccepted,
		Body: dto.KnowledgeQASessionResponse{
			Session: toSessionDTO(session),
			Turn:    toTurnDTO(turn),
		},
	}, nil
}

func (handler *turnHandler) StopTurn(request api.WorkspaceScopedRequest) (api.Response, error) {
	sessionID := routeParameter(request.Gin, "sessionId")
	turnID := routeParameter(request.Gin, "turnId")
	if sessionID == "" || turnID == "" {
		return api.Response{}, invalidRequest("sessionId and turnId are required")
	}
	if _, err := requireIdempotencyKey(request.Gin); err != nil {
		return api.Response{}, err
	}
	session, err := handler.deps.Sessions.LoadSessionForOwner(
		request.Context(), request.WorkspaceID(), request.UserID(), sessionID,
	)
	if err != nil {
		return storeFailure(err)
	}
	turn, err := handler.deps.Sessions.StopTurnForOwner(
		request.Context(), request.WorkspaceID(), request.UserID(), turnID,
		time.Now().UTC(),
	)
	if err != nil {
		return storeFailure(err)
	}
	if turn.SessionID != sessionID {
		return api.Response{}, api.ErrNotFound
	}
	handler.notifyTurnUpdated(
		request.Context(), session.WorkspaceID, session.OwnerMemberID, session.ThreadChannelID, turn.ID,
	)
	if handler.deps.Queue != nil {
		handler.deps.Queue.CancelKnowledgeQATurn(
			context.WithoutCancel(request.Context()), turn.ID,
			turn.CurrentAttemptID,
		)
	}
	return api.OK(toTurnDTO(turn)), nil
}

func (handler *turnHandler) ListTurns(request api.WorkspaceScopedRequest) (api.Response, error) {
	threadID := routeParameter(request.Gin, "threadId")
	if threadID == "" {
		return api.Response{}, invalidRequest("threadId is required")
	}
	turns, err := handler.deps.Sessions.ListTurnsForOwner(
		request.Context(), request.WorkspaceID(), request.UserID(), threadID, 100,
	)
	if err != nil {
		return storeFailure(err)
	}
	if handler.deps.Feedbacks == nil {
		return api.Response{}, errors.New("Knowledge Q&A feedback service is unavailable")
	}
	feedback, err := handler.deps.Feedbacks.ListFeedbackForThread(
		request.Context(), request.WorkspaceID(), request.UserID(), threadID,
	)
	if err != nil {
		return api.Response{}, err
	}
	feedbackByTurnID := make(map[string]Feedback, len(feedback))
	for _, item := range feedback {
		feedbackByTurnID[item.TurnID] = item
	}
	result := make([]dto.KnowledgeQATurn, 0, len(turns))
	for _, turn := range turns {
		turnDTO := toTurnDTO(turn)
		if item, ok := feedbackByTurnID[turn.ID]; ok {
			feedbackDTO := toFeedbackDTO(item)
			turnDTO.Feedback = &feedbackDTO
		}
		result = append(result, turnDTO)
	}
	return api.OK(dto.KnowledgeQATurnListResponse{Turns: result}), nil
}
