// Package approval owns generic versioned approval instances, assignee facts,
// and idempotent terminal decision receipts. Business domains retain their
// request and materialization state and consume Approval decisions by version.
package approval

import (
	"context"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

const (
	StatePending   = "pending"
	StateApproved  = "approved"
	StateRejected  = "rejected"
	StateCancelled = "cancelled"
	StateExpired   = "expired"

	AssignmentPending    = "pending"
	AssignmentApproved   = "approved"
	AssignmentRejected   = "rejected"
	AssignmentCancelled  = "cancelled"
	AssignmentSuperseded = "superseded"
	AssignmentExpired    = "expired"

	DecisionApproved  = "approved"
	DecisionRejected  = "rejected"
	DecisionCancelled = "cancelled"
	DecisionExpired   = "expired"
)

type Instance struct {
	ID                string
	WorkspaceID       string
	SubjectType       string
	SubjectRef        string
	SubjectVersion    int64
	Payload           []byte
	PayloadDigest     string
	IdempotencyKey    string
	PendingKey        string
	ApplicantMemberID string
	State             string
	Decision          string
	DecisionReason    string
	DecidedByMemberID string
	Version           int64
	ProjectedVersion  int64
	CorrelationID     string
	CreatedAt         time.Time
	UpdatedAt         time.Time
	DecidedAt         *time.Time
	ExpiresAt         *time.Time
}

type Assignment struct {
	ID                 string
	WorkspaceID        string
	ApprovalInstanceID string
	ApproverMemberID   string
	Status             string
	Version            int64
	CreatedAt          time.Time
	UpdatedAt          time.Time
	ClosedAt           *time.Time
}

type DecisionReceipt struct {
	ID                 string
	WorkspaceID        string
	ApprovalInstanceID string
	AssignmentID       string
	ActorMemberID      string
	ClientDecisionID   string
	RequestDigest      string
	Decision           string
	Reason             string
	ResultVersion      int64
	CreatedAt          time.Time
}

type CreateParams struct {
	ID                  string
	WorkspaceID         string
	SubjectType         string
	SubjectRef          string
	SubjectVersion      int64
	Payload             []byte
	PayloadDigest       string
	IdempotencyKey      string
	PendingKey          string
	ApplicantMemberID   string
	AllowAgentApplicant bool
	ApproverMemberIDs   []string
	CorrelationID       string
	ExpiresAt           *time.Time
	Now                 time.Time
}

type DecideParams struct {
	WorkspaceID      string
	ApprovalID       string
	AssignmentID     string
	ActorMemberID    string
	ExpectedVersion  int64
	ClientDecisionID string
	Decision         string
	Reason           string
	Now              time.Time
}

type ApplicantCancelParams struct {
	WorkspaceID          string
	ApprovalID           string
	ActorMemberID        string
	ExpectedVersion      int64
	ClientCancellationID string
	Reason               string
	Now                  time.Time
}

// ApprovedStaleParams reconciles an approved decision whose owning domain
// deterministically found its frozen subject stale. The original decision
// receipt remains the audit record of the requested action.
type ApprovedStaleParams struct {
	WorkspaceID      string
	ApprovalID       string
	SubjectType      string
	ActorMemberID    string
	ClientDecisionID string
	ApprovedVersion  int64
	Reason           string
	Now              time.Time
}

// DomainCancelParams cancels a pending Approval after the owning business
// domain has authorized ActorMemberID. It is intentionally available only on
// TxStore so the domain mutation and generic terminal Approval facts share one
// transaction.
type DomainCancelParams struct {
	WorkspaceID          string
	ApprovalID           string
	ActorMemberID        string
	ExpectedVersion      int64
	ClientCancellationID string
	Reason               string
	Now                  time.Time
}

type DecisionResult struct {
	Instance       Instance
	Assignment     Assignment
	Receipt        DecisionReceipt
	AlreadyApplied bool
}

// DecisionEffect runs a subject-specific mutation after generic Approval
// authorization and version checks, but before the terminal Approval facts are
// written. Returning an error rolls the caller-owned transaction back.
type DecisionEffect func(
	ctx context.Context,
	tx coredb.Tx,
	instance Instance,
	decision string,
) (DecisionEffectResult, error)

// DecisionEffectResult may override an approved decision with a generic
// terminal state. The stale-subject path uses cancelled so the pending subject
// key is released without applying an obsolete payload.
type DecisionEffectResult struct {
	TerminalState  string
	DecisionReason string
}
