package knowledgeqa

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"

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

type feedbackStore struct {
	store *reporting.Store
}

func NewFeedbackService(store *reporting.Store) FeedbackService {
	return &feedbackStore{store: store}
}

type knowledgeQAFeedbackContext struct {
	SchemaVersion int                            `json:"schemaVersion"`
	Turn          knowledgeQAFeedbackTurnContext `json:"turn"`
	Activities    []dto.KnowledgeQAActivity      `json:"activities"`
	Citations     []knowledgeQAFeedbackCitation  `json:"citations"`
}

type knowledgeQAFeedbackTurnContext struct {
	UserMessageID             string                    `json:"userMessageId"`
	AssistantMessageID        string                    `json:"assistantMessageId"`
	RequestedKnowledgeBaseIDs []string                  `json:"requestedKnowledgeBaseIds"`
	RequestedSourceIDs        []string                  `json:"requestedSourceIds"`
	AuthorizedRoots           []knowledgeQAFeedbackRoot `json:"authorizedRoots"`
	RequestedModel            string                    `json:"requestedModel"`
	RequestedReasonLevel      string                    `json:"requestedReasonLevel"`
	EffectiveModel            string                    `json:"effectiveModel"`
	EffectiveReasonLevel      string                    `json:"effectiveReasonLevel"`
	OrganizationPolicyVersion int64                     `json:"organizationPolicyVersion"`
	RuntimeVersion            string                    `json:"runtimeVersion"`
	PromptVersion             string                    `json:"promptVersion"`
	AttemptID                 string                    `json:"attemptId"`
	Status                    string                    `json:"status"`
	ErrorCode                 string                    `json:"errorCode,omitempty"`
}

type knowledgeQAFeedbackRoot struct {
	KnowledgeBaseID string `json:"knowledgeBaseId"`
	SourceID        string `json:"sourceId,omitempty"`
	RootKind        string `json:"rootKind"`
	RepoRole        string `json:"repoRole"`
	Revision        string `json:"revision"`
	FreshnessStatus string `json:"freshnessStatus"`
}

type knowledgeQAFeedbackCitation struct {
	ID              string `json:"id"`
	KnowledgeBaseID string `json:"knowledgeBaseId"`
	SourceID        string `json:"sourceId,omitempty"`
	RootKind        string `json:"rootKind"`
	RepoRole        string `json:"repoRole"`
	Revision        string `json:"revision"`
	ResolvedRef     string `json:"resolvedRef,omitempty"`
	ResolvedRefKind string `json:"resolvedRefKind,omitempty"`
	Path            string `json:"path"`
	LineStart       int64  `json:"lineStart"`
	LineEnd         int64  `json:"lineEnd"`
}

func (service *feedbackStore) UpsertFeedback(
	ctx context.Context,
	params UpsertFeedbackParams,
) (Feedback, error) {
	if service == nil || service.store == nil {
		return Feedback{}, fmt.Errorf("Knowledge Q&A feedback service is not configured")
	}
	contextJSON, err := json.Marshal(feedbackContext(params.Snapshot))
	if err != nil {
		return Feedback{}, fmt.Errorf("encode Knowledge Q&A feedback context: %w", err)
	}
	stored, err := service.store.UpsertKnowledgeQAFeedback(ctx, reporting.UpsertKnowledgeQAFeedbackParams{
		WorkspaceID:     params.Snapshot.Turn.WorkspaceID,
		OrganizationID:  params.Snapshot.Turn.OrganizationID,
		ThreadChannelID: params.Snapshot.ThreadChannelID,
		SessionID:       params.Snapshot.Turn.SessionID,
		TurnID:          params.Snapshot.Turn.ID,
		ReporterUserID:  params.ReporterUserID,
		Rating:          params.Rating,
		Reasons:         params.Reasons,
		Comment:         params.Comment,
		Question:        params.Snapshot.Turn.Question,
		Answer:          params.Snapshot.Answer,
		ContextJSON:     contextJSON,
		Now:             params.Now,
	})
	if err != nil {
		return Feedback{}, feedbackStoreError(err)
	}
	return feedbackFromStore(stored), nil
}

func (service *feedbackStore) ListFeedbackForThread(
	ctx context.Context,
	workspaceID string,
	reporterUserID string,
	threadChannelID string,
) ([]Feedback, error) {
	if service == nil || service.store == nil {
		return nil, fmt.Errorf("Knowledge Q&A feedback service is not configured")
	}
	stored, err := service.store.ListKnowledgeQAFeedbackForThread(
		ctx, workspaceID, reporterUserID, threadChannelID,
	)
	if err != nil {
		return nil, feedbackStoreError(err)
	}
	result := make([]Feedback, 0, len(stored))
	for _, item := range stored {
		result = append(result, feedbackFromStore(item))
	}
	return result, nil
}

func (service *feedbackStore) DeleteFeedback(
	ctx context.Context,
	workspaceID string,
	reporterUserID string,
	turnID string,
) error {
	if service == nil || service.store == nil {
		return fmt.Errorf("Knowledge Q&A feedback service is not configured")
	}
	return feedbackStoreError(service.store.DeleteKnowledgeQAFeedback(
		ctx, workspaceID, reporterUserID, turnID,
	))
}

func feedbackContext(snapshot FeedbackSnapshot) knowledgeQAFeedbackContext {
	activities := make([]dto.KnowledgeQAActivity, 0, len(snapshot.Activities))
	for _, activity := range snapshot.Activities {
		activities = append(activities, toActivityDTO(activity))
	}
	roots := make([]knowledgeQAFeedbackRoot, 0, len(snapshot.Turn.AuthorizedRoots))
	for _, root := range snapshot.Turn.AuthorizedRoots {
		roots = append(roots, knowledgeQAFeedbackRoot{
			KnowledgeBaseID: root.KnowledgeBaseID,
			SourceID:        root.SourceID,
			RootKind:        root.RootKind,
			RepoRole:        root.RepoRole,
			Revision:        root.Revision,
			FreshnessStatus: root.FreshnessStatus,
		})
	}
	citations := make([]knowledgeQAFeedbackCitation, 0, len(snapshot.Citations))
	for _, citation := range snapshot.Citations {
		citations = append(citations, knowledgeQAFeedbackCitation{
			ID:              citation.ID,
			KnowledgeBaseID: citation.KnowledgeBaseID,
			SourceID:        citation.SourceID,
			RootKind:        citation.RootKind,
			RepoRole:        citation.RepoRole,
			Revision:        citation.Revision,
			ResolvedRef:     citation.ResolvedRef,
			ResolvedRefKind: citation.ResolvedRefKind,
			Path:            citation.Path,
			LineStart:       citation.LineStart,
			LineEnd:         citation.LineEnd,
		})
	}
	turn := snapshot.Turn
	return knowledgeQAFeedbackContext{
		SchemaVersion: 1,
		Turn: knowledgeQAFeedbackTurnContext{
			UserMessageID:             turn.UserMessageID,
			AssistantMessageID:        turn.AssistantMessageID,
			RequestedKnowledgeBaseIDs: nonNilStrings(turn.RequestedKnowledgeBaseIDs),
			RequestedSourceIDs:        nonNilStrings(turn.RequestedSourceIDs),
			AuthorizedRoots:           roots,
			RequestedModel:            turn.RequestedModel,
			RequestedReasonLevel:      turn.RequestedReasonLevel,
			EffectiveModel:            turn.EffectiveModel,
			EffectiveReasonLevel:      turn.EffectiveReasonLevel,
			OrganizationPolicyVersion: turn.OrganizationPolicyVersion,
			RuntimeVersion:            turn.RuntimeVersion,
			PromptVersion:             turn.PromptVersion,
			AttemptID:                 turn.CurrentAttemptID,
			Status:                    turn.Status,
			ErrorCode:                 turn.ErrorCode,
		},
		Activities: activities,
		Citations:  citations,
	}
}

func feedbackFromStore(stored reporting.KnowledgeQAFeedback) Feedback {
	return Feedback{
		ID:        stored.ID,
		TurnID:    stored.TurnID,
		Rating:    stored.Rating,
		Reasons:   append([]string(nil), stored.Reasons...),
		Comment:   stored.Comment,
		UpdatedAt: stored.UpdatedAt,
	}
}

func feedbackStoreError(err error) error {
	if err == nil {
		return nil
	}
	if errors.Is(err, reporting.ErrBadRequest) {
		return fmt.Errorf("%w: %v", ErrFeedbackInvalid, err)
	}
	return err
}
