package agent

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/gin-gonic/gin"

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

// ReviewContextStore is the review storage surface needed by the agent-runtime
// review-context endpoints. Satisfied by *review.Store.
type ReviewContextStore interface {
	CreateReviewContextForAgent(ctx context.Context, authenticatedAgent agent.AuthenticatedAgent, params agent.UpsertReviewContextParams) (agent.ReviewContext, error)
	GetReviewContext(ctx context.Context, workspaceID string, id string) (*agent.ReviewContext, error)
	EnsureAgentThreadAccess(ctx context.Context, workspaceID string, agentID string, threadID string) error
	EnsureAgentTaskAccess(ctx context.Context, authenticatedAgent agent.AuthenticatedAgent, taskScopeID string) error
	ListReviewContextsForThreadForAgent(ctx context.Context, workspaceID string, threadID string) ([]agent.ReviewContext, error)
	ListReviewContextsForThreadForAgentPage(ctx context.Context, workspaceID string, threadID string, options agent.ReviewContextListOptions) (agent.ReviewContextPage, error)
	ListReviewContextsForTaskForAgent(ctx context.Context, authenticatedAgent agent.AuthenticatedAgent, taskScopeID string) ([]agent.ReviewContext, error)
	DeleteReviewContextForAgent(ctx context.Context, authenticatedAgent agent.AuthenticatedAgent, id string) error
}

func (h *ReviewContextHandler) validateReviewContext(ctx context.Context, authenticatedAgent agent.AuthenticatedAgent, params agent.UpsertReviewContextParams) error {
	if h.validator == nil {
		return runtimecontrol.ErrMachineOffline
	}
	machineID := strings.TrimSpace(params.MachineID)
	if machineID == "" {
		machineID = authenticatedAgent.MachineID
	}
	// Route validation through the ReviewSummary RPC — NOT through
	// ValidateAgentOutputTarget, whose desktop-side validator (workspace.
	// ValidateOutputTarget) trims the canonical rootPath before Stat. That
	// trim would rewrite "/repo " to "/repo" and break the trailing-
	// whitespace isolation guarantee the Review cache key depends on.
	//
	// The desktop's loadReviewRepoState treats empty refs as a repo-only
	// health check: it confirms the repo opens and HEAD resolves without
	// computing a diff or resolving a base/head pair. So this single call
	// validates BOTH "is this a git repo?" and (when refs are present) "are
	// these refs resolvable?". RootPath flows through byte-exact — no trim
	// on the canonical path anywhere in this chain.
	summary, err := h.validator.GetAgentReviewSummary(ctx, machineID, authenticatedAgent.AgentID, params.RootPath, params.BaseRef, params.HeadRef, "")
	if err != nil {
		if errors.Is(err, runtimecontrol.ErrMachineOffline) {
			return err
		}
		return badRequest(err.Error())
	}
	if strings.TrimSpace(summary.Status) == "" || strings.TrimSpace(summary.Status) == "ready" {
		return nil
	}
	if message := strings.TrimSpace(summary.Message); message != "" {
		return badRequest(message)
	}
	return badRequest(fmt.Sprintf("review context is not ready for status %q", strings.TrimSpace(summary.Status)))
}

// reviewParamsFromLegacyRequest converts the legacy AgentReviewContextRequest
// (kept for backward compatibility) into UpsertReviewContextParams. Directory-kind
// targets are rejected (the directory feature was not shipped).
func reviewParamsFromLegacyRequest(request dto.AgentReviewContextRequest) (agent.UpsertReviewContextParams, error) {
	kind := strings.TrimSpace(request.Kind)
	if kind != "" && kind != "git_repo" {
		return agent.UpsertReviewContextParams{}, badRequest("only git_repo review targets are supported")
	}
	params := agent.UpsertReviewContextParams{
		RootPath:    request.RootPath,
		BaseRef:     request.BaseRef,
		HeadRef:     request.HeadRef,
		DisplayName: request.DisplayName,
		PRURL:       request.PRURL,
	}
	if request.TaskScope != nil {
		if strings.TrimSpace(request.TaskScope.Kind) != agent.ScopeTypeTask || !strings.HasPrefix(strings.TrimSpace(request.TaskScope.ScopeID), "tsk_") {
			return agent.UpsertReviewContextParams{}, badRequest("taskScope must be {kind:'task',scopeId:'tsk_...'}")
		}
		params.TaskScopeID = strings.TrimSpace(request.TaskScope.ScopeID)
	}
	return params, nil
}

func (h *ReviewContextHandler) conversationContext(c *gin.Context) (agent.AuthenticatedAgent, string, string, bool) {
	authenticatedAgent, ok := authenticate(c, h.auth)
	if !ok {
		return agent.AuthenticatedAgent{}, "", "", false
	}
	subjectType := strings.TrimSpace(c.Param("subjectType"))
	subjectID := strings.TrimSpace(c.Param("subjectId"))
	if subjectType == "" {
		c.JSON(400, agentError("subjectType is required"))
		return agent.AuthenticatedAgent{}, "", "", false
	}
	if subjectID == "" {
		c.JSON(400, agentError("subjectId is required"))
		return agent.AuthenticatedAgent{}, "", "", false
	}
	return authenticatedAgent, subjectType, subjectID, true
}
