package mailruntime

import (
	"context"
	"encoding/json"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/extension/providers/mail/mailstore"
)

const (
	actionSearchMessages     = "search-messages"
	actionGetMessage         = "get-message"
	actionListAttachments    = "list-attachments"
	actionGetAttachment      = "get-attachment"
	actionCreateDraft        = "create-draft"
	actionUpdateDraft        = "update-draft"
	actionCreateReplyDraft   = "create-reply-draft"
	actionCreateForwardDraft = "create-forward-draft"
	actionSendDraft          = "send-draft"

	maxSearchResults      = 50
	maxAttachmentMetadata = 100
	defaultSearchLimit    = 20

	ScopeMetadataRead = "mail.metadata.read"
	ScopeContentRead  = "mail.content.read"
	ScopeDraftWrite   = "mail.draft.write"
	ScopeSend         = "mail.send"
)

// Invocation is the trusted server envelope produced after Catalog,
// authorization, schema, Installation, and Connection preflight.
type Invocation struct {
	WorkspaceID                    string
	AgentID                        string
	AgentMemberID                  string
	ServiceInstallationID          string
	ServiceConnectionID            string
	ServiceConnectionGeneration    string
	ServiceInstallationExtensionID string
	AuthorizedByMemberID           string
	ExtensionName                  string
	Action                         string
	DefinitionVersion              int
	DefinitionDigest               string
	CatalogVersion                 int64
	RequestHash                    string
	Input                          json.RawMessage
}

// Invoker is implemented by Service and consumed by the Mail Builtin adapter.
type Invoker interface {
	Invoke(context.Context, Invocation) (json.RawMessage, error)
}

// ConnectionLoader resolves the exact active server-selected Connection.
type ConnectionLoader interface {
	LoadActiveConnection(
		context.Context,
		string,
		string,
		string,
	) (mailstore.Connection, error)
}

// SearchFilters is the closed provider-neutral filter contract.
type SearchFilters struct {
	Text           string     `json:"text,omitempty"`
	Folder         string     `json:"folder,omitempty"`
	From           []string   `json:"from,omitempty"`
	To             []string   `json:"to,omitempty"`
	ReceivedAfter  *time.Time `json:"receivedAfter,omitempty"`
	ReceivedBefore *time.Time `json:"receivedBefore,omitempty"`
	Unread         *bool      `json:"unread,omitempty"`
	HasAttachments *bool      `json:"hasAttachments,omitempty"`
}

// Body is trusted draft input. Mailbox reads use protocol.Body so their
// sanitization and truncation markers cannot be lost.
type Body struct {
	Format  string `json:"format"`
	Content string `json:"content"`
}

// DraftContent is trusted draft input. Outbound attachments are resolved from
// the AgentTeam attachment authority before any mailbox mutation.
type DraftContent struct {
	To      []string `json:"to,omitempty"`
	CC      []string `json:"cc,omitempty"`
	BCC     []string `json:"bcc,omitempty"`
	Subject string   `json:"subject,omitempty"`
	Body    Body     `json:"body"`
}

// LoadedAttachment is one AgentTeam attachment the invoking Agent is authorized to
// read, resolved by the trusted AttachmentLoader before any mutation.
type LoadedAttachment struct {
	Filename  string
	MediaType string
	Blob      []byte
}

// AttachmentLoader resolves AgentTeam attachment IDs through the member-scoped
// read authority. Implementations must not accept paths or provider URLs.
type AttachmentLoader interface {
	LoadAgentAttachment(
		ctx context.Context,
		workspaceID string,
		agentMemberID string,
		attachmentID string,
	) (LoadedAttachment, error)
}

// AttachmentUpload is one bounded inbound attachment to materialize.
type AttachmentUpload struct {
	Filename  string
	MediaType string
	Blob      []byte
}

// MaterializedAttachment is the AgentTeam-side projection of one inbound
// attachment, addressed only by its AgentTeam attachment ID.
type MaterializedAttachment struct {
	AttachmentID string
	Filename     string
	MediaType    string
	SizeBytes    int64
}

// InvocationAuthorization is the trusted envelope re-validated by the fresh
// pre-mutation fence. Every field is server-resolved.
type InvocationAuthorization struct {
	WorkspaceID          string
	AgentID              string
	AgentMemberID        string
	AuthorizedByMemberID string
	InstallationID       string
	ConnectionID         string
	ConnectionGeneration string
	SelectionID          string
	ExtensionName        string
	Action               string
	DefinitionDigest     string
	DefinitionVersion    int
	CatalogVersion       int64
	RequiredScopes       []string
}

// InvocationAuthorizer is the fresh fence re-resolving the App installation,
// Extension selection, pinned definition digest, and the Agent's explicit
// authorization decision immediately before a provider or AgentTeam-side mutation.
// A Connection-only reload cannot detect a revoked or disabled App.
type InvocationAuthorizer interface {
	ReauthorizeInvocation(
		ctx context.Context,
		authorization InvocationAuthorization,
	) error
}

// AttachmentMaterializer projects one inbound attachment into the invoking
// Agent's single writable conversation target after the store re-authorizes
// the explicit target against the trusted Agent identity.
type AttachmentMaterializer interface {
	MaterializeAgentAttachment(
		ctx context.Context,
		workspaceID string,
		agentID string,
		agentMemberID string,
		target string,
		upload AttachmentUpload,
	) (MaterializedAttachment, error)
}
