package approvalapp

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

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/workflow/run"
	"github.com/tidbcloud/AgentTeam/internal/workflow/orchestrator"
)

type workflowRunFeedbackMessageWriter interface {
	SendMessageForUserWithOptions(context.Context, model.User, string, string, string, []string, workflowRunHumanMessageCreateOptions) (workflowRunAppMessage, workflowRunMessageDelivery, error)
}

type workflowRunFeedbackMessageAtomicWriter interface {
	WriteMessageForUserInTx(context.Context, coredb.Tx, model.User, string, string, string, workflowRunHumanMessageCreateOptions) (workflowRunAppMessage, workflowRunMessageDelivery, error)
}

func workflowRunApprovalComment(input map[string]any) (string, bool, error) {
	for key := range input {
		if key != "comment" {
			return "", false, fmt.Errorf("Workflow Run approval action input contains unsupported field %q", key)
		}
	}
	if input == nil {
		return "", false, nil
	}
	value, present := input["comment"]
	if !present {
		return "", false, nil
	}
	comment, ok := value.(string)
	if !ok {
		return "", true, fmt.Errorf("Workflow Run approval comment must be a string")
	}
	comment = strings.TrimSpace(comment)
	if len([]rune(comment)) > orchestrator.ApprovalDecisionCommentMaxRunes {
		return "", true, fmt.Errorf("Workflow Run approval comment is too long")
	}
	return comment, true, nil
}

func workflowRunApprovalFeedbackPreCommitMutation(
	writer workflowRunFeedbackMessageAtomicWriter,
	actorUserID, actorMemberID, workspaceID, effectID, decision, comment string,
) orchestrator.ApprovalDecisionPreCommitMutation {
	return func(
		ctx context.Context,
		tx coredb.Tx,
		run run.Run,
		_ run.Activation,
		effect run.Effect,
		resultJSON *json.RawMessage,
	) error {
		if writer == nil || tx == nil || resultJSON == nil {
			return fmt.Errorf("Workflow Run approval feedback atomic writer is not configured")
		}
		if strings.TrimSpace(run.WorkspaceID) != strings.TrimSpace(workspaceID) ||
			strings.TrimSpace(effect.ID) != strings.TrimSpace(effectID) {
			return fmt.Errorf("Workflow Run approval feedback identity changed")
		}
		request, err := orchestrator.DecodeEffectRequest(effect.RequestJSON)
		if err != nil {
			return fmt.Errorf("decode frozen Workflow Run approval feedback scope: %w", err)
		}
		if request.Intent.FeedbackScope == nil {
			return fmt.Errorf("Workflow Run approval comment has no frozen feedback scope")
		}
		actorUserID, actorMemberID, workspaceID = strings.TrimSpace(actorUserID), strings.TrimSpace(actorMemberID), strings.TrimSpace(workspaceID)
		if actorUserID == "" || actorMemberID == "" || workspaceID == "" {
			return fmt.Errorf("Workflow Run approval feedback Human identity is required")
		}
		clientMessageID := workflowApprovalFeedbackClientMessageIDFor(effect.ID, decision)
		created, delivery, err := writer.WriteMessageForUserInTx(
			ctx, tx, model.User{ID: actorUserID}, workspaceID,
			request.Intent.FeedbackScope.ScopeID, comment,
			workflowRunHumanMessageCreateOptions{
				ClientMessageID: clientMessageID, ExpectedAuthorMemberID: actorMemberID,
				RequireLiveHandoff: true,
				Source: workflowRunContributionSource(workflowRunContributionSourceParams{
					RunID: run.ID, EffectID: effect.ID, PlanID: run.PlanID,
					WorkflowName: run.WorkflowNameSnapshot, SourceKind: "approval_feedback",
				}),
			},
		)
		if err != nil {
			return fmt.Errorf("write Workflow Run approval feedback atomically: %w", err)
		}
		if strings.TrimSpace(created.ID) == "" || strings.TrimSpace(delivery.MessageID) != strings.TrimSpace(created.ID) {
			return fmt.Errorf("Workflow Run approval feedback writer returned an inconsistent receipt")
		}
		if created.AuthorProfile.ActorType != workflowRunHumanActorType ||
			strings.TrimSpace(created.AuthorProfile.MemberID) != actorMemberID ||
			strings.TrimSpace(created.ClientMessageID) != clientMessageID {
			return fmt.Errorf("Workflow Run approval feedback writer returned an invalid Human receipt")
		}
		receipt, err := orchestrator.DecodeEffectResult(*resultJSON)
		if err != nil {
			return fmt.Errorf("decode Workflow Run approval effect receipt: %w", err)
		}
		receipt.ApprovalFeedbackMessageID = created.ID
		receipt.ApprovalFeedbackClientMessageID = clientMessageID
		encoded, err := orchestrator.EncodeEffectResult(receipt)
		if err != nil {
			return fmt.Errorf("encode Workflow Run approval feedback receipt: %w", err)
		}
		*resultJSON = encoded
		return nil
	}
}

func (handler *Handler) sendWorkflowRunFeedback(
	ctx context.Context,
	workspaceID, actorUserID, actorMemberID string,
	result orchestrator.ApprovalDecisionResult,
) error {
	comment := strings.TrimSpace(result.Comment)
	if comment == "" || strings.TrimSpace(result.FeedbackMessageID) != "" {
		// New approvals already persisted the ordinary message and durable outbox
		// in ResolveAwaitingEffect's transaction. Legacy completed receipts lack
		// the receipt field and continue through the replay-safe writer below.
		return nil
	}
	if result.FeedbackScope == nil {
		return fmt.Errorf("Workflow Run approval comment has no frozen feedback scope")
	}
	if handler == nil || handler.projector == nil || handler.projector.notifications == nil {
		return fmt.Errorf("Workflow Run approval feedback message writer is not configured")
	}
	writer, ok := handler.projector.notifications.(workflowRunFeedbackMessageWriter)
	if !ok {
		return fmt.Errorf("Workflow Run approval feedback message writer is not configured")
	}
	actorUserID = strings.TrimSpace(actorUserID)
	actorMemberID = strings.TrimSpace(actorMemberID)
	workspaceID = strings.TrimSpace(workspaceID)
	if actorUserID == "" || actorMemberID == "" || workspaceID == "" {
		return fmt.Errorf("Workflow Run approval feedback Human identity is required")
	}
	_, _, err := writer.SendMessageForUserWithOptions(
		ctx,
		model.User{ID: actorUserID},
		workspaceID,
		result.FeedbackScope.ScopeID,
		comment,
		nil,
		workflowRunHumanMessageCreateOptions{
			ClientMessageID:        workflowApprovalFeedbackClientMessageID(result),
			ExpectedAuthorMemberID: actorMemberID,
		},
	)
	if err != nil {
		return fmt.Errorf("send Workflow Run approval feedback: %w", err)
	}
	return nil
}

func workflowApprovalFeedbackClientMessageID(result orchestrator.ApprovalDecisionResult) string {
	return workflowApprovalFeedbackClientMessageIDFor(result.EffectID, result.Decision)
}

func workflowApprovalFeedbackClientMessageIDFor(effectID, decision string) string {
	return "workflow-approval-feedback:" + strings.TrimSpace(effectID) + ":" + strings.TrimSpace(decision)
}
