package runmanage

import (
	"context"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	workflowrunstore "github.com/tidbcloud/AgentTeam/internal/store/workflow/run"
)

// AgentPrincipal is the authenticated Agent identity that must accompany every
// Agent-facing Run observation. MemberID is the canonical Agent member bound
// to AgentID at authentication time.
type AgentPrincipal = workflowrunstore.AgentPrincipal

// AgentRunAuthorizer is implemented by the production Run repository. It
// applies current machine-owner authority against the immutable Run principal;
// callback participation and invocation provenance are not Run grants. Keeping
// this capability separate from the human Repository interface prevents a
// human caller from accidentally inheriting Agent-specific policy.
type AgentRunAuthorizer interface {
	AuthorizeAgentRun(context.Context, AgentPrincipal, string, string) error
}

type AgentRunLister interface {
	ListRunsForAgent(context.Context, workflowrunstore.ListRunsParams, AgentPrincipal) ([]workflowrunstore.Run, error)
}

type AgentRunCanceler interface {
	CancelRunForAgent(context.Context, workflowrunstore.CancelRunParams, AgentPrincipal) (workflowrunstore.Run, *workflowrunstore.Event, error)
}

func (service *Service) authorizeAgentRun(ctx context.Context, principal AgentPrincipal, workspaceID, runID string) error {
	workspaceID = strings.TrimSpace(workspaceID)
	if principal.WorkspaceID == "" {
		principal.WorkspaceID = workspaceID
	}
	if principal.WorkspaceID != workspaceID || strings.TrimSpace(principal.AgentID) == "" || strings.TrimSpace(principal.MemberID) == "" {
		return mapRepositoryError(storeerr.NotFound("Workflow Run not found"))
	}
	authorizer, ok := service.repository.(AgentRunAuthorizer)
	if !ok {
		return mapRepositoryError(storeerr.NotFound("Workflow Run not found"))
	}
	if err := authorizer.AuthorizeAgentRun(ctx, principal, workspaceID, strings.TrimSpace(runID)); err != nil {
		return mapRepositoryError(err)
	}
	return nil
}

func (service *Service) ListRunsForAgent(ctx context.Context, input ListRunsInput, principal AgentPrincipal) (RunPage, error) {
	if principal.WorkspaceID == "" {
		principal.WorkspaceID = input.WorkspaceID
	}
	lister, ok := service.repository.(AgentRunLister)
	if !ok {
		return RunPage{}, mapRepositoryError(storeerr.NotFound("Workflow Run not found"))
	}
	return service.listRuns(ctx, input, func(ctx context.Context, params workflowrunstore.ListRunsParams) ([]workflowrunstore.Run, error) {
		return lister.ListRunsForAgent(ctx, params, principal)
	})
}

func (service *Service) GetRunForAgent(ctx context.Context, workspaceID, runID string, principal AgentPrincipal) (workflowrunstore.Run, error) {
	if err := service.authorizeAgentRun(ctx, principal, workspaceID, runID); err != nil {
		return workflowrunstore.Run{}, err
	}
	return service.GetRun(ctx, workspaceID, runID)
}

func (service *Service) ReadArtifactPageForAgent(ctx context.Context, input ArtifactReadInput, principal AgentPrincipal) (ArtifactPage, error) {
	if err := service.authorizeAgentRun(ctx, principal, input.WorkspaceID, input.RunID); err != nil {
		return ArtifactPage{}, err
	}
	return service.ReadArtifactPage(ctx, input)
}

func (service *Service) ListActivationsForAgent(ctx context.Context, input ListChildrenInput, principal AgentPrincipal) (ActivationPage, error) {
	if err := service.authorizeAgentRun(ctx, principal, input.WorkspaceID, input.RunID); err != nil {
		return ActivationPage{}, err
	}
	return service.ListActivations(ctx, input)
}

func (service *Service) ListEffectsForAgent(ctx context.Context, input ListChildrenInput, principal AgentPrincipal) (EffectPage, error) {
	if err := service.authorizeAgentRun(ctx, principal, input.WorkspaceID, input.RunID); err != nil {
		return EffectPage{}, err
	}
	return service.ListEffects(ctx, input)
}

func (service *Service) ListEventsForAgent(ctx context.Context, input ListEventsInput, principal AgentPrincipal) (EventPage, error) {
	if err := service.authorizeAgentRun(ctx, principal, input.WorkspaceID, input.RunID); err != nil {
		return EventPage{}, err
	}
	return service.ListEvents(ctx, input)
}

func (service *Service) GetEventForAgent(ctx context.Context, workspaceID, runID string, sequence int64, principal AgentPrincipal) (workflowrunstore.Event, error) {
	if err := service.authorizeAgentRun(ctx, principal, workspaceID, runID); err != nil {
		return workflowrunstore.Event{}, err
	}
	return service.GetEvent(ctx, workspaceID, runID, sequence)
}

func (service *Service) CancelForAgent(ctx context.Context, input CancelInput, principal AgentPrincipal) (CancelResult, error) {
	workspaceID := strings.TrimSpace(input.WorkspaceID)
	if principal.WorkspaceID == "" {
		principal.WorkspaceID = workspaceID
	}
	if err := service.authorizeAgentRun(ctx, principal, workspaceID, input.RunID); err != nil {
		return CancelResult{}, err
	}
	canceler, ok := service.repository.(AgentRunCanceler)
	if !ok {
		return CancelResult{}, mapRepositoryError(storeerr.NotFound("Workflow Run not found"))
	}
	// The repository repeats this owner check under the Run/placement lock in
	// CancelRunForAgent. This preflight is only an early non-disclosing failure;
	// it is not relied on for mutation authorization.
	input.Actor = Actor{Type: ActorTypeAgent, ID: principal.AgentID, MemberID: principal.MemberID}
	return service.cancelUsing(ctx, input, func(ctx context.Context, params workflowrunstore.CancelRunParams) (workflowrunstore.Run, *workflowrunstore.Event, error) {
		return canceler.CancelRunForAgent(ctx, params, principal)
	})
}
