// Package collaborator owns the Human "request to become a collaborator"
// lifecycle on top of the generic Approval authority. taskboard retains the
// durable request row; this service orchestrates Approval create/decide/cancel
// and the canonical grant primitive so an approved request is equivalent to a
// direct owner-add.
package collaborator

import (
	"context"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/approval"
	"github.com/tidbcloud/AgentTeam/internal/store/taskboard"
)

const (
	// SubjectType is the Approval subject type for collaborator requests.
	SubjectType = "collaborator_request"
	// RequestType is the registered submit request type.
	RequestType = "collaborator_request"

	// DefaultApprovalValidity is exactly seven days. The brief mandates this
	// default because no existing unified Approval expiry convention of at least
	// seven days was found. It is independent of the collaborator expiry.
	DefaultApprovalValidity = 7 * 24 * time.Hour
)

// SubmitCommand is the transport-neutral request to become a collaborator.
type SubmitCommand struct {
	WorkspaceID           string
	RequesterUserID       string
	ScopeType             string
	ScopeID               string
	Role                  string
	CollaboratorExpiresAt *time.Time
	ClientRequestID       string
	Now                   time.Time
}

// SubmitResult reports the created (or replayed) request.
type SubmitResult struct {
	ApprovalID string
	RequestID  string
	Status     string
	Created    bool
}

// CancelCommand is a requester-initiated cancellation.
type CancelCommand struct {
	WorkspaceID          string
	RequesterUserID      string
	ApprovalID           string
	ExpectedVersion      int64
	ClientCancellationID string
	Reason               string
	Now                  time.Time
}

// ApprovalPayload is the deterministic JSON frozen into the Approval instance. It
// carries every decision-relevant fact so the decision effect revalidates
// against an immutable baseline.
type ApprovalPayload struct {
	SchemaVersion         string     `json:"schemaVersion"`
	RequesterMemberID     string     `json:"requesterMemberId"`
	RequesterUserID       string     `json:"requesterUserId"`
	OwnerMemberID         string     `json:"ownerMemberId"`
	ScopeType             string     `json:"scopeType"`
	ScopeID               string     `json:"scopeId"`
	ScopeDisplayLabel     string     `json:"scopeDisplayLabel"`
	Role                  string     `json:"role"`
	CollaboratorExpiresAt *time.Time `json:"collaboratorExpiresAt,omitempty"`
	ApprovalExpiresAt     *time.Time `json:"approvalExpiresAt,omitempty"`
	Effect                string     `json:"effect"`
	RequestID             string     `json:"requestId"`
}

// TaskboardTx is the transaction-bound taskboard port. The service binds it to
// the same transaction as the Approval so request row and Approval facts commit
// or roll back together.
type TaskboardTx interface {
	// ResolveCollaboratorRequestTarget resolves the canonical target and the
	// sole approver (execution owner) for a Human requester. It returns a NoOp
	// marker when the requester is already an active collaborator or is the
	// owner (a request must not create an Approval in that case). Invisible or
	// invalid targets surface the established non-enumerating not-found/forbidden
	// response.
	ResolveCollaboratorRequestTarget(context.Context, taskboard.CollaboratorRequestTargetInput) (taskboard.CollaboratorRequestTarget, error)
	// LookupRequesterMemberID resolves the requester's active Human member +
	// user identity (for the frozen payload).
	LookupRequesterIdentity(context.Context, string, string) (taskboard.CollaboratorRequesterIdentity, error)
	// CreateCollaboratorRequest writes the durable request row in the bound
	// transaction (after the Approval row was created).
	CreateCollaboratorRequest(context.Context, taskboard.CreateCollaboratorRequestParams) error
	// LoadCollaboratorRequest locks and returns the request row.
	LoadCollaboratorRequest(context.Context, string) (taskboard.CollaboratorRequest, error)
	// ApplyApprovedCollaboratorRequest revalidates frozen facts under locks and
	// applies the canonical grant (shared primitive), exactly once.
	ApplyApprovedCollaboratorRequest(context.Context, taskboard.CollaboratorRequest) error
	// TerminalizeCollaboratorRequest sets the request lifecycle status.
	TerminalizeCollaboratorRequest(context.Context, string, string, *time.Time) error
}

// Taskboard is the taskboard-side port the service drives. *taskboard.Store
// satisfies it structurally (InTx returns a *taskboard.TxStore with all
// TaskboardTx methods).
type Taskboard interface {
	InTx(coredb.Tx) TaskboardTx
	// ListRequesterCollaboratorRequests lists a requester's request statuses.
	ListRequesterCollaboratorRequests(context.Context, string, string) ([]taskboard.CollaboratorRequest, error)
}

// DecideResult is the decision outcome surfaced to the Approval subject.
type DecideResult struct {
	ApprovalID string
	RequestID  string
	Status     string
}

// ReplayResult is the read-only idempotency lookup.
type ReplayResult struct {
	ApprovalID string
	RequestID  string
	Status     string
	Found      bool
}

var _ = approval.DecideParams{}
