package agent

import (
	"strings"
	"time"
)

const (
	AgentLLMModeLocal      = "local"
	AgentLLMModeAgentTeamProxy  = "agentteam_proxy"
	AgentLLMModeAgentTeamHosted = "agentteam_hosted"

	AgentLLMBindingLocal      = AgentLLMModeLocal
	AgentLLMBindingAgentTeamProxy  = AgentLLMModeAgentTeamProxy
	AgentLLMBindingAgentTeamHosted = AgentLLMModeAgentTeamHosted

	AgentLLMConfigErrorUnreadable = "unreadable"

	AgentPermissionModeWorkspaceWrite = "agent_workspace_write"
	AgentPermissionModeFullAccess     = "full_access"

	legacyAgentPermissionModeRestricted = "restricted"
)

func AgentTeamProxyProviderID(workspaceID string) string {
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	if trimmedWorkspaceID == "" {
		return AgentLLMModeAgentTeamProxy
	}
	return AgentLLMModeAgentTeamProxy + "_" + trimmedWorkspaceID
}

func IsAgentTeamProxyProviderID(providerID string) bool {
	trimmedProviderID := strings.TrimSpace(providerID)
	return trimmedProviderID == AgentLLMModeAgentTeamProxy || strings.HasPrefix(trimmedProviderID, AgentLLMModeAgentTeamProxy+"_")
}

func NormalizeAgentLLMMode(value string) string {
	switch strings.TrimSpace(value) {
	case AgentLLMModeAgentTeamProxy:
		return AgentLLMModeAgentTeamProxy
	case AgentLLMModeAgentTeamHosted:
		return AgentLLMModeAgentTeamHosted
	case "", AgentLLMModeLocal:
		return AgentLLMModeLocal
	default:
		return AgentLLMModeLocal
	}
}

// DefaultAgentPermissionMode returns the runtime default when value is empty and
// coerces legacy restricted/workspace-only values to full access.
func DefaultAgentPermissionMode(value string) string {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return AgentPermissionModeFullAccess
	}
	if normalized, ok := NormalizeAgentPermissionMode(trimmed); ok {
		return normalized
	}
	return trimmed
}

// NormalizeAgentPermissionMode maps accepted agent permission-mode inputs onto
// the only currently supported runtime behavior: full filesystem access.
// TODO(agent-permission-mode): remove legacy aliases after the rolling-upgrade
// window where old clients and cached daemon payloads may still send them.
func NormalizeAgentPermissionMode(value string) (string, bool) {
	switch strings.TrimSpace(value) {
	case AgentPermissionModeFullAccess, AgentPermissionModeWorkspaceWrite, legacyAgentPermissionModeRestricted:
		return AgentPermissionModeFullAccess, true
	default:
		return "", false
	}
}

// IsAgentPermissionMode reports whether value is an accepted current or legacy
// agent runtime permission mode.
func IsAgentPermissionMode(value string) bool {
	_, ok := NormalizeAgentPermissionMode(value)
	return ok
}

type AgentRuntimeFailureSummary struct {
	EventClass string
	MessageID  string
	Summary    string
	Detail     string
	OccurredAt time.Time
}

type AppAgentInstance struct {
	ID             string
	MachineID      string
	SessionID      string
	Status         string
	Activity       string
	ActivityDetail string
	SourceKind     string
	SourceRef      string
	StartedAt      time.Time
	LastSeenAt     time.Time
}

const MaxAgentRuntimeSnapshotIDs = 20

const (
	DefaultAgentDirectoryLimit = 50
	MaxAgentDirectoryLimit     = 100
	DefaultAgentSearchLimit    = 20
	MaxAgentSearchLimit        = 50
	MaxAgentDirectoryLookupIDs = 50
	MaxAgentRuntimeFailureIDs  = 20
)

type AgentDirectoryOptions struct {
	Scope                     string
	Limit                     int
	Cursor                    string
	Query                     string
	IncludeReview             bool
	IncludeTeamAssignment     bool
	IncludeCurrentRoleBinding bool
}

type AgentDirectoryItem struct {
	ID                 string
	Name               string
	MemberID           string
	DisplayName        string
	AvatarURL          string
	OwnerUserID        string
	MachineID          string
	CanManage          bool
	CanReview          bool
	TeamID             string
	TeamDisplayName    string
	CurrentRoleBinding *AgentRoleBindingSummary
	InChannel          *bool
}

type AgentDirectoryPage struct {
	Agents     []AgentDirectoryItem
	Limit      int
	Cursor     string
	HasMore    bool
	NextCursor string
}

type AgentRuntimeSnapshot struct {
	AgentID         string
	MachineID       string
	CanManage       bool
	MachineStatus   string
	AutoWakeEnabled bool
	Status          string
	Activity        string
	ActivityDetail  string
	CurrentInstance *AppAgentInstance
}

// AppAgent is the agent shape expected by the web app client.
type AgentRoleBindingSummary struct {
	BindingKind           string
	RoleID                string
	RoleVersionID         string
	SourcePresetRoleID    string
	SourcePresetVersionID string
	ActiveDefinitionPath  string
	RealizationID         string
}

type AgentRoleMaterializationSource struct {
	Kind                  string
	BindingKind           string
	RoleID                string
	RoleVersionID         string
	PresetID              string
	SourcePresetRoleID    string
	SourcePresetVersionID string
	Locale                string
}

type AgentRoleMaterializationItem struct {
	Type        string
	Name        string
	Description string
	Required    bool
	Action      string
	ResourceID  string
	AccessLevel string
	Branch      string
	Content     string
}

type AgentRoleMaterialization struct {
	RoleID               string
	RoleVersionID        string
	RealizationID        string
	ActiveDefinitionPath string
	ManifestItems        []AgentRoleMaterializationItem
}

type AgentRoleMaterializationInput struct {
	UserID         string
	WorkspaceID    string
	AgentID        string
	BindingID      string
	Source         AgentRoleMaterializationSource
	RoleID         string
	RoleVersionID  string
	RealizationID  string
	DefinitionPath string
	ManifestItems  []AgentRoleMaterializationItem
}

type AppAgent struct {
	ID                   string
	Name                 string
	MemberID             string
	OwnerUserID          string
	DisplayName          string
	AvatarURL            string
	Description          string
	Runtime              string
	PermissionMode       string
	Model                string
	ReasoningEffort      string
	LLMMode              string
	BaseURL              string
	Headers              map[string]string
	WireAPI              string
	APIKeyConfigured     bool
	LLMConfigError       string
	ModelProfileID       string
	MachineID            string
	MachineStatus        string
	SourceKind           string
	SourceRef            string
	CurrentRoleBinding   *AgentRoleBindingSummary
	CanManage            bool
	CanReview            bool
	CanDisable           bool
	AutoWakeEnabled      bool
	Status               string
	Activity             string
	ActivityDetail       string
	CurrentInstance      *AppAgentInstance
	CreatedAt            time.Time
	LatestRuntimeFailure *AgentRuntimeFailureSummary
}

// AppAgentListOptions controls optional filtering and hydration for
// browser-facing agent roster reads. Zero values preserve the legacy
// unbounded, lightweight workspace list.
type AppAgentListOptions struct {
	OwnedOnly              bool
	ChannelID              string
	Limit                  int
	IncludeRuntimeFailures bool
}

// AppAgentDetailOptions controls optional hydration for single browser-facing
// agent reads.
type AppAgentDetailOptions struct {
	IncludeRuntimeFailure bool
}

// BehaviorSettings is the server-owned behavior configuration for one agent.
type BehaviorSettings struct {
	WorkspaceID                   string
	AgentID                       string
	AccountVisibleReplyContract   bool
	VisibleReplyContractEnabled   bool
	EffectiveVisibleReplyContract bool
}

// AccountBehaviorSettings is the account-level behavior configuration.
type AccountBehaviorSettings struct {
	UserID                      string
	VisibleReplyContractEnabled bool
	RuntimeDebugEnabled         bool
}

// AccountBehaviorSettingsUpdate is a partial account behavior settings update.
type AccountBehaviorSettingsUpdate struct {
	VisibleReplyContractEnabled *bool
	RuntimeDebugEnabled         *bool
}

// Empty reports whether the update carries no changed fields.
func (update AccountBehaviorSettingsUpdate) Empty() bool {
	return update.VisibleReplyContractEnabled == nil &&
		update.RuntimeDebugEnabled == nil
}

// AgentView is the public API agent item.
type AgentView struct {
	AgentID              string
	MemberID             string
	DisplayName          string
	Description          string
	Runtime              string
	PermissionMode       string
	Model                string
	ReasoningEffort      string
	LLMMode              string
	BaseURL              string
	APIKey               string
	Headers              map[string]string
	WireAPI              string
	LLMConfigError       string
	ModelProfileID       string
	MachineID            string
	SourceKind           string
	SourceRef            string
	AutoWakeEnabled      bool
	Status               string
	Activity             string
	ActivityDetail       string
	SessionID            string
	LatestRuntimeFailure *AgentRuntimeFailureSummary
}

// CreateAgentParams is the store agent creation payload. Name is an optional
// public handle; when it is empty, the store derives a unique handle from
// DisplayName. AutoWakeEnabled defaults to true when omitted. Created agents
// always use full filesystem access.
type CreateAgentParams struct {
	Name            string
	DisplayName     string
	AvatarURL       string
	Description     string
	Runtime         string
	Model           string
	ReasoningEffort string
	LLMMode         string
	BaseURL         string
	APIKey          string
	Headers         map[string]string
	WireAPI         string
	ModelProfileID  string
	SourceKind      string
	SourceRef       string
	AutoWakeEnabled *bool
	ChannelIDs      []string
}

// UpdateAgentParams is the public API patch payload. DeriveName
// makes an explicit display-name rename update the public handle in the same
// transaction; ordinary display-name metadata patches keep the handle intact.
type UpdateAgentParams struct {
	Name            *string
	DisplayName     *string
	DeriveName      bool
	Description     *string
	AvatarURL       *string
	Runtime         *string
	PermissionMode  *string
	Model           *string
	ReasoningEffort *string
	LLMMode         *string
	BaseURL         *string
	APIKey          *string
	Headers         map[string]string
	HeadersSet      bool
	WireAPI         *string
	LLMSet          bool
	ModelProfileID  *string
	ModelProfileSet bool
}

type WorkspaceMaterialization struct {
	RoleID               string
	SourceKind           string
	SourceRef            string
	ActiveDefinitionPath string
	SeedFiles            []WorkspaceSeedFile
}

type WorkspaceSeedFile struct {
	Path    string
	Content string
}

// WorkspaceSnapshot is the JSON body for `/workspace`.
type WorkspaceSnapshot struct {
	Channels   []WorkspaceChannel
	Agents     []WorkspaceActor
	Humans     []WorkspaceActor
	Limits     WorkspaceSnapshotLimits
	Kind       string
	Limit      int
	HasMore    bool
	NextCursor string
}

// WorkspaceSnapshotRequest narrows agent workspace discovery to one bounded
// kind/page instead of dumping every visible channel and actor collection.
type WorkspaceSnapshotRequest struct {
	Kind   string
	Query  string
	Limit  int
	Cursor string
}

// WorkspaceSnapshotLimits reports server-side safety caps applied to each `/workspace` collection.
type WorkspaceSnapshotLimits struct {
	Channels WorkspaceSnapshotCollectionLimit
	Agents   WorkspaceSnapshotCollectionLimit
	Humans   WorkspaceSnapshotCollectionLimit
}

// WorkspaceSnapshotCollectionLimit reports whether one collection was truncated.
type WorkspaceSnapshotCollectionLimit struct {
	Limit     int
	Truncated bool
}

// WorkspaceChannel lists a channel and whether the agent has joined it.
type WorkspaceChannel struct {
	Name        string
	DisplayName string
	Description string
	Joined      bool
}

// WorkspaceActor lists a member identity in `/workspace`.
type WorkspaceActor struct {
	ID             string
	Name           string
	DisplayName    string
	Status         string
	Activity       string
	ActivityDetail string
	MachineName    string
	MachineHost    string
	MachineID      string
	OwnerName      string
}

// WorkspaceFile describes one tree node returned by the daemon.
type WorkspaceFile struct {
	Name           string
	Path           string
	IsDirectory    bool
	Size           int64
	ModifiedAt     *time.Time
	ChildDirCount  int
	ChildFileCount int
}

const (
	// DefaultWorkspaceListLimit is the default page size for daemon workspace
	// directory browse requests.
	DefaultWorkspaceListLimit = 50
	// MaxWorkspaceListLimit is the maximum page size for daemon workspace
	// directory browse requests.
	MaxWorkspaceListLimit = 200
)

// WorkspaceListOptions bounds one daemon workspace directory browse request.
type WorkspaceListOptions struct {
	Limit  int
	Cursor string
}

// WorkspaceFilePage is one bounded daemon workspace directory browse page.
type WorkspaceFilePage struct {
	Files      []WorkspaceFile
	RootPath   string
	Limit      int
	Cursor     string
	HasMore    bool
	NextCursor string
}

// MachineDirectory describes one browsable directory row returned by the
// machine directory browsing endpoint.
type MachineDirectory struct {
	Name string
	Path string
}

// MachineDirectoryPage is one bounded machine directory browse page. Cursor
// and NextCursor carry the canonical raw offset form. Truncated reports
// whether the browse hit the per-directory raw-entry budget before exhausting
// the directory.
type MachineDirectoryPage struct {
	CurrentPath string
	ParentPath  string
	Directories []MachineDirectory
	Limit       int
	Cursor      string
	HasMore     bool
	NextCursor  string
	Truncated   bool
}

// WorkspaceFileContent is the daemon response to `agent:workspace:read`.
type WorkspaceFileContent struct {
	Content *string
	Binary  bool
}

// WorkspaceDirectory is the daemon response item for machine scan.
type WorkspaceDirectory struct {
	DirectoryName  string
	TotalSizeBytes int64
	LastModified   time.Time
	FileCount      int64
}

// WorkspaceDirectoryPage is one bounded daemon machine workspace scan page.
type WorkspaceDirectoryPage struct {
	Directories []WorkspaceDirectory
	Limit       int
	Cursor      string
	HasMore     bool
	NextCursor  string
}

type AgentReviewSummary struct {
	Status               string
	Message              string
	RepositoryRoot       string
	Branch               string
	DefaultBranch        string
	BaseRef              string
	HeadRef              string
	ComparisonMode       string
	AvailableRefs        []string
	CompareRef           string
	AvailableCompareRefs []string
	HeadCommit           string
	MergeBaseCommit      string
	AheadCount           int
	BehindCount          int
	Dirty                bool
	BranchFileCount      int
	BranchAdditionCount  int
	BranchDeletionCount  int
	WorkingAdditionCount int
	WorkingDeletionCount int
	StagedFileCount      int
	UnstagedFileCount    int
	LastScannedAt        time.Time
}

type AgentReviewFile struct {
	Path               string
	OldPath            string
	ChangeType         string
	Staged             bool
	Unstaged           bool
	StagedChangeType   string
	UnstagedChangeType string
	// Token is the opaque per-file token mirrored from
	// desktop types.ReviewFile.Token. Equal tokens imply the file's
	// review-relevant state is unchanged since the last fetch that produced
	// the same token, so the frontend can skip re-fetching the per-file
	// diff. Strength (authoritative vs hint) follows the enclosing
	// fingerprint's WorkingStrength.
	Token string
}

type AgentReviewSnapshot struct {
	Summary             AgentReviewSummary
	Files               []AgentReviewFile
	WorkingFiles        []AgentReviewFile
	InitialDiffs        map[string]AgentReviewDiff
	InitialWorkingDiffs map[string]AgentReviewDiff
}

// AgentReviewFingerprintWorkingStrengthHint matches
// desktop types.ReviewFingerprintWorkingStrengthHint. The string is duplicated
// here so the hub does not need to import the desktop module just to read the
// constant.
const AgentReviewFingerprintWorkingStrengthHint = "hint"

// AgentReviewFingerprint mirrors desktop types.ReviewFingerprint. The hub
// never interprets token equality itself — it only forwards the fingerprint
// to the frontend, which decides whether to re-fetch the full snapshot.
type AgentReviewFingerprint struct {
	HeadCommit        string
	BaseRef           string
	HeadRef           string
	ComparisonMode    string
	RepositoryToken   string
	BranchToken       string
	WorkingToken      string
	WorkingStrength   string
	ObservedAt        time.Time
	NextRefreshAt     time.Time
	WorkingStateFresh bool
	BranchFileCount   int
	StagedFileCount   int
	UnstagedFileCount int
}

type AgentReviewDiffSection struct {
	Key        string
	Label      string
	Diff       string
	OldText    string
	NewText    string
	ChangeType string
	Message    string
	Binary     bool
	TooLarge   bool
}

type AgentReviewDiff struct {
	Status     string
	Message    string
	Path       string
	OldPath    string
	ChangeType string
	Sections   []AgentReviewDiffSection
}

// Review context scope type. Review contexts are thread-bound in the current
// model; channel/DM/agent polymorphic scopes were removed with the single-table
// review_contexts schema.
const (
	ScopeTypeThread = "thread"
	ScopeTypeTask   = "task"
)

// ReviewContext is a workspace-scoped git-compare review resource. It carries
// one optional ThreadID or TaskScopeID and a SourceAgentID (the agent that
// produced it). One row = one review context; ID is the resource id used
// everywhere (MCP / UI / summary / files / diff). Visibility is decided in the
// store layer by the canonical subject authority.
type ReviewContext struct {
	ID              string
	WorkspaceID     string
	ThreadID        string
	TaskScopeID     string
	SourceAgentID   string
	MachineID       string
	RootPath        string
	BaseRef         string
	HeadRef         string
	DisplayName     string
	PRURL           string
	ProjectID       string
	CreatedByMember string
	UpdatedByMember string
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

// ReviewContextListOptions bounds a review-context collection read using its
// stable updatedAt DESC, id DESC ordering.
type ReviewContextListOptions struct {
	Limit           int
	CursorUpdatedAt time.Time
	CursorID        string
}

type ReviewContextPage struct {
	Contexts []ReviewContext
	HasMore  bool
}

// UpsertReviewContextParams creates or updates a review context.
type UpsertReviewContextParams struct {
	WorkspaceID     string
	ThreadID        string
	TaskScopeID     string
	SourceAgentID   string
	MachineID       string
	RootPath        string
	BaseRef         string
	HeadRef         string
	DisplayName     string
	PRURL           string
	ProjectID       string
	CreatedByMember string
	UpdatedByMember string
}
