package webagent

import (
	"context"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/api/daemon"
	"github.com/tidbcloud/AgentTeam/internal/runtimecontrol"
	"github.com/tidbcloud/AgentTeam/internal/service/agentmemory"
	"github.com/tidbcloud/AgentTeam/internal/service/maas"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/app/runtime"
	"github.com/tidbcloud/AgentTeam/internal/store/organization"
	"github.com/tidbcloud/AgentTeam/internal/store/rewind"
)

const maxAgentDirectoryLookupIDs = agent.MaxAgentDirectoryLookupIDs

// AgentDirectoryLookupStore is the bounded exact-ID projection used to
// restore saved Agent resource labels without enumerating the Workspace.
type AgentStore interface {
	ListAgentsForWorkspace(ctx context.Context, userID, workspaceID string) ([]agent.AppAgent, error)
	ListOwnedAgentsForWorkspace(ctx context.Context, userID, workspaceID string) ([]agent.AppAgent, error)
	ListAgentsForWorkspaceWithOptions(ctx context.Context, userID, workspaceID string, options agent.AppAgentListOptions) ([]agent.AppAgent, error)
	ListAgentDirectoryForWorkspace(ctx context.Context, userID, workspaceID string, options agent.AgentDirectoryOptions) (agent.AgentDirectoryPage, error)
	LookupAgentDirectoryForWorkspace(ctx context.Context, userID, workspaceID string, agentIDs []string) ([]agent.AgentDirectoryItem, error)
	AgentDetailForWorkspace(ctx context.Context, userID, workspaceID, agentID string) (agent.AppAgent, error)
	AgentRuntimeSnapshotsForWorkspace(ctx context.Context, userID, workspaceID string, agentIDs []string) ([]agent.AgentRuntimeSnapshot, error)
	AgentRuntimeFailureForWorkspace(ctx context.Context, userID, workspaceID string, agentID string) (*agent.AgentRuntimeFailureSummary, error)
	AgentRuntimeFailuresForWorkspace(ctx context.Context, userID, workspaceID string, agentIDs []string) (map[string]agent.AgentRuntimeFailureSummary, error)
	CreateAgentForUser(ctx context.Context, userID, workspaceID, machineID string, params agent.CreateAgentParams) (agent.AgentView, error)
	AgentForWorkspace(ctx context.Context, userID, workspaceID, agentID string, options ...agent.AppAgentDetailOptions) (agent.AppAgent, error)
	GetAgentForUser(ctx context.Context, userID, workspaceID, agentID string) (agent.AgentView, error)
	CopyAgentRoleBindingForUser(ctx context.Context, userID, workspaceID, sourceAgentID, targetAgentID string) error
	UpsertAgentRoleMaterialization(ctx context.Context, input agent.AgentRoleMaterializationInput) error
	UpdateAgentForUser(ctx context.Context, userID, workspaceID, agentID string, params agent.UpdateAgentParams) (agent.AgentView, error)
	RestoreAgentPermissionModeForUser(ctx context.Context, userID, workspaceID, agentID, permissionMode, expectedPermissionMode string) error
	DeleteAgentForWorkspace(ctx context.Context, userID, workspaceID, agentID string) (string, error)
	SetAgentAutoWakeForUser(ctx context.Context, userID, workspaceID, agentID string, autoWake bool) (agent.AgentView, error)
	GetAgentWorkspaceReaderForUser(ctx context.Context, userID, workspaceID, agentID string) (agent.AgentView, error)
	ClearAgentSessionForWorkspace(ctx context.Context, userID, workspaceID, agentID string) error
	RequireMachineManagementForUser(ctx context.Context, userID, workspaceID, machineID string) error
}

// AgentBehaviorSettingsStore owns browser-facing server behavior settings for
// individual agents.
type AgentBehaviorSettingsStore interface {
	AgentBehaviorSettingsForUser(ctx context.Context, userID string, workspaceID string, agentID string) (agent.BehaviorSettings, error)
}

// ModelProfileStore owns current-user reusable local runtime configurations.
type ModelProfileStore interface {
	ListModelProfilesForUser(ctx context.Context, userID string) ([]agent.ModelProfile, error)
	CreateModelProfileForUser(ctx context.Context, userID string, params agent.CreateModelProfileParams) ([]agent.ModelProfile, error)
	UpdateModelProfileForUser(ctx context.Context, userID string, profileID string, params agent.UpdateModelProfileParams) ([]agent.ModelProfile, error)
	GetModelProfileForUser(ctx context.Context, userID string, profileID string) (agent.ModelProfile, error)
	DeleteModelProfileForUser(ctx context.Context, userID string, profileID string) error
}

// RewindStore is the rewind-domain storage port used by web-agent handlers.
// Satisfied by *rewind.Store.
type RewindStore interface {
	ListAllAgentRecoveryPointsForWorkspace(ctx context.Context, userID, workspaceID, agentID string) ([]rewind.MessageRecoveryPoint, error)
	DeleteAllAgentRecoveryPointsForWorkspace(ctx context.Context, userID, workspaceID, agentID string) ([]rewind.MessageRecoveryPoint, error)
}

type CloudMemoryProvisioner interface {
	EnableAgentCloudMemory(ctx context.Context, input agentmemory.EnableInput) error
}

// ReviewStore is the review-domain storage port used by web agent handlers.
// Satisfied by *review.Store.
type ReviewStore interface {
	GetAgentReviewReaderForUser(ctx context.Context, userID, workspaceID, agentID string) (agent.AgentView, error)
	CreateReviewContextForUser(ctx context.Context, userID string, params agent.UpsertReviewContextParams) (agent.ReviewContext, error)
	GetReviewContextForUser(ctx context.Context, userID, workspaceID, id string) (*agent.ReviewContext, error)
	ListReviewContextsForThread(ctx context.Context, userID, workspaceID, threadID string) ([]agent.ReviewContext, error)
	ListVisibleReviewContextsForUser(ctx context.Context, userID, workspaceID string) ([]agent.ReviewContext, error)
	ListReviewContextsForProject(ctx context.Context, workspaceID string, projectID string) ([]agent.ReviewContext, error)
	UpdateReviewContextForUser(ctx context.Context, userID, workspaceID, id string, params agent.UpsertReviewContextParams) (*agent.ReviewContext, error)
	DeleteReviewContextForUser(ctx context.Context, userID, workspaceID, id string) error
}

// ProjectStore is the persistence + authorization surface for Project
// resources. Methods follow the *ForUser convention: the store resolves the
// caller's workspace member and enforces the owner/manage/view role matrix
// described in agent.ProjectRole* constants. Handler code MUST NOT do its own
// permission checks — see planner seq=513 contract point 5.
type ProjectStore interface {
	CreateProjectForUser(ctx context.Context, userID string, params agent.CreateProjectParams) (agent.Project, error)
	GetProjectForUser(ctx context.Context, userID, workspaceID, projectID string) (agent.Project, error)
	ListProjectPageForUser(ctx context.Context, userID, workspaceID string, options agent.ProjectListOptions) (agent.ProjectPage, error)
	UpdateProjectForUser(ctx context.Context, userID, workspaceID, projectID string, params agent.UpdateProjectParams) (agent.Project, error)
	DeleteProjectForUser(ctx context.Context, userID, workspaceID, projectID string) error

	ListProjectAccessForUser(ctx context.Context, userID, workspaceID, projectID string) ([]agent.ProjectGrant, error)
	UpsertProjectGrantForUser(ctx context.Context, userID, workspaceID, projectID, targetMemberID, role string) (agent.ProjectGrant, error)
	DeleteProjectGrantForUser(ctx context.Context, userID, workspaceID, projectID, targetMemberID string) error
}

// MachineAccessChecker verifies the caller may manage a target machine.
// Project creation reuses the legacy Directory create authorization path so
// that an arbitrary workspace member cannot create a Project pointing at a
// machine/root they do not control and then read its files via owner-only
// access (planner seq=538). The production implementation is the same
// AgentStore.RequireMachineManagementForUser used by the directory API.
type MachineAccessChecker interface {
	RequireMachineManagementForUser(ctx context.Context, userID, workspaceID, machineID string) error
}

type Runtime interface {
	MachineConnected(machineID string) bool
	MachineRuntimeCatalog(machineID string) ([]string, map[string]runtime.RuntimeDefaults, bool)
	AgentRuntimeRunning(machineID string, agentID string) bool
	AgentRuntimeSessionID(machineID string, agentID string) (string, bool)
	AgentPermissionModeSupported(machineID string) bool
	AgentMaaSDirectSupport(machineID string) (supported bool, known bool)
	StartAgent(ctx context.Context, machineID string, agentID string) error
	RestartAgent(ctx context.Context, machineID string, agentID string) error
	StopAgent(machineID string, agentID string) error
	InterruptAgentRun(machineID string, agentID string) error
	ResetAgentSession(machineID string, agentID string) error
	UpdateAgentIdentity(machineID string, agentID string, name string, displayName string) error
	SyncMachineCatalog(ctx context.Context, machineID string) error
	NotifyMemoryAccessChangedByAgent(ctx context.Context, workspaceID string, agentID string) error
	ResetAgentWorkspace(machineID string, agentID string) error
	ListAgentWorkspace(ctx context.Context, machineID string, agentID string, dirPath string) ([]agent.WorkspaceFile, error)
	ListAgentWorkspacePage(ctx context.Context, machineID string, agentID string, dirPath string, opts agent.WorkspaceListOptions) (agent.WorkspaceFilePage, error)
	ReadAgentWorkspaceFile(ctx context.Context, machineID string, agentID string, path string) (agent.WorkspaceFileContent, error)
	GetAgentMemorySyncStatus(ctx context.Context, machineID string, agentID string) (daemon.AgentMemorySyncStatus, bool, error)
	RetryAgentMemorySync(ctx context.Context, machineID string, agentID string) (bool, error)
	ListAgentOutputTargetFiles(ctx context.Context, machineID string, agentID string, targetPath string, dirPath string) ([]agent.WorkspaceFile, error)
	ListAgentOutputTargetFilesPage(ctx context.Context, machineID string, agentID string, targetPath string, dirPath string, opts agent.WorkspaceListOptions) (agent.WorkspaceFilePage, error)
	ReadAgentOutputTargetFile(ctx context.Context, machineID string, agentID string, targetPath string, path string) (agent.WorkspaceFileContent, error)
	GetAgentReviewSummary(ctx context.Context, machineID string, agentID string, targetPath string, baseRef string, headRef string, comparisonMode string) (agent.AgentReviewSummary, error)
	GetAgentReviewSnapshot(ctx context.Context, machineID string, agentID string, targetPath string, baseRef string, headRef string, comparisonMode string) (agent.AgentReviewSnapshot, error)
	GetAgentReviewFingerprint(ctx context.Context, machineID string, agentID string, targetPath string, baseRef string, headRef string, comparisonMode string) (agent.AgentReviewFingerprint, error)
	ListAgentReviewFiles(ctx context.Context, machineID string, agentID string, targetPath string, scope string, baseRef string, headRef string, comparisonMode string) ([]agent.AgentReviewFile, error)
	GetAgentReviewDiff(ctx context.Context, machineID string, agentID string, targetPath string, scope string, path string, baseRef string, headRef string, comparisonMode string) (agent.AgentReviewDiff, error)
	GetAgentReviewDiffBatch(ctx context.Context, machineID string, agentID string, targetPath string, scope string, paths []string, baseRef string, headRef string, comparisonMode string) (map[string]daemon.AgentReviewDiffBatchResult, error)
	ValidateAgentOutputTarget(ctx context.Context, machineID string, agentID string, kind string, targetPath string) error
	GetAgentTrajectoryHistory(ctx context.Context, machineID string, agentID string, limit int, before int64) ([]runtimecontrol.AgentTrajectoryItem, error)
	GCRecoveryPoints(machineID string, agentID string, points []rewind.MessageRecoveryPoint) error
}

// RuntimeCommands owns browser runtime start/stop/reset orchestration.
type RuntimeCommands interface {
	StartAgentRuntimeForUser(ctx context.Context, userID string, workspaceID string, agentID string) (agent.AgentView, error)
	StopAgentRuntimeForUser(ctx context.Context, userID string, workspaceID string, agentID string) (agent.AgentView, error)
	ResetAgentRuntimeForUser(ctx context.Context, userID string, workspaceID string, agentID string, clearWorkspace bool) error
}

// Realtime emits browser websocket updates for agent events.
type Realtime interface {
	EmitAgentCreated(workspaceID string)
	EmitAgentDeleted(workspaceID string)
	EmitAgentActivity(workspaceID string, ownerUserID string, agentID string, status string, detail string)
}

var ValidateReviewParams = agent.ValidateReviewParams

// OrganizationProvider validates org-managed providers visible to a workspace.
type OrganizationProvider interface {
	ListVisibleOrganizationProvidersForWorkspace(ctx context.Context, workspaceID string) ([]organization.OrganizationProviderView, error)
	CreateInternalWorkspaceProxyKey(ctx context.Context, workspaceID string, expiresAt time.Time) (string, error)
}

// HostedAgentValidator enforces hosted model admission for browser saves.
type HostedAgentValidator interface {
	ValidateAgentBindingForUser(ctx context.Context, params maas.AgentBindingValidation) error
}
