package ports

import (
	"context"
	"errors"
	"time"

	"github.com/nexuscrm/shared/pkg/models"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionauthoringv1"
)

const (
	PermissionAuthoringCommandAccepted    = "ACCEPTED"
	PermissionAuthoringCommandCompleted   = "COMPLETED"
	PermissionAuthoringConvergenceNone    = "NONE"
	PermissionAuthoringConvergencePending = "PENDING"
)

var (
	ErrPermissionAuthoringCommandConflict  = errors.New("permission authoring command identity is already bound to different content")
	ErrPermissionAuthoringCommandMissing   = errors.New("permission authoring command is missing")
	ErrPermissionAuthoringBindingConflict  = errors.New("permission authoring command is already bound to a different PermissionSet")
	ErrPermissionAuthoringAdmissionExpired = errors.New("permission authoring command recovery window expired")
)

// PermissionAuthoringCommandAcceptance is the immutable, principal-scoped
// identity accepted before mutable target lookup or business authorization.
// It contains no per-family progress because partial completion is not a
// representable state of the aggregate command.
type PermissionAuthoringCommandAcceptance struct {
	RowID                   string
	ScopeDigest             string
	CommandID               string
	RequestDigest           string
	TargetKind              string
	TargetID                string
	ExpectedAuthoringToken  string
	SemanticMappingRevision uint32
	AdmissionTTL            time.Duration
	OutcomeRetention        time.Duration
}

// PermissionAuthoringCommand is the persistence-neutral journal projection.
// ResultPayload is the opaque immutable semantic response owned by
// nexuscrm-permissions.
type PermissionAuthoringCommand struct {
	RowID                    string
	ScopeDigest              string
	CommandID                string
	RequestDigest            string
	TargetKind               string
	TargetID                 string
	CanonicalPermissionSetID string
	ExpectedAuthoringToken   string
	SemanticMappingRevision  uint32
	State                    string
	ResultPayload            []byte
	AdmissionExpiresAt       time.Time
	OutcomeExpiresAt         time.Time
	OutcomeRetentionMicros   int64
}

// PermissionAuthoringCommandStore is the durable journal seam consumed by the
// Permissions aggregate owner. Acceptance and canonical binding are allowed to
// commit before execution. Business mutation and terminal completion must use
// the same outer transaction.
type PermissionAuthoringCommandStore interface {
	Accept(ctx context.Context, input PermissionAuthoringCommandAcceptance) (PermissionAuthoringCommand, bool, error)
	LoadActive(ctx context.Context, scopeDigest, commandID string) (PermissionAuthoringCommand, error)
	// LockCommand acquires the command row and rejects an ACCEPTED command whose
	// database-owned recovery window has expired. Callers may therefore perform
	// business work only after this lock succeeds.
	LockCommand(ctx context.Context, rowID string) (PermissionAuthoringCommand, error)
	BindCanonicalPermissionSet(ctx context.Context, rowID, permissionSetID string) error
	TerminalOutcomeExpiry(ctx context.Context, rowID string) (time.Time, error)
	CompleteBound(ctx context.Context, rowID string, resultPayload []byte, outcomeExpiresAt time.Time) error
	CompleteUnboundRejection(ctx context.Context, rowID string, resultPayload []byte, outcomeExpiresAt time.Time) error
}

// PermissionAuthoringConvergenceEvent is the durable, persistence-neutral
// disposition produced by one applied aggregate command. CommandRowID is an
// immutable source identity, not a retention dependency: delivery state owns
// a separate lifetime from the command journal's terminal result.
type PermissionAuthoringConvergenceEvent struct {
	CommandRowID           string
	CommandID              string
	CommandDigest          string
	CanonicalPermissionSet string
	AuthoringRevision      uint64
}

// PermissionAuthoringConvergence persists recovery work in the aggregate
// transaction. Wake is only a post-commit latency optimization: recovery must
// be driven from the durable pending disposition when a process dies before
// the hook runs.
type PermissionAuthoringConvergence interface {
	EnqueuePermissionAuthoringConvergence(ctx context.Context, event PermissionAuthoringConvergenceEvent) error
	WakePermissionAuthoringConvergence()
}

type PermissionAuthoringConvergenceWork struct {
	Event        PermissionAuthoringConvergenceEvent
	AttemptCount uint64
}

// PermissionAuthoringConvergenceStore is the recovery side of the durable
// disposition. LeaseOwner must be unique per claim batch so more than one
// worker can recover pending rows without treating an in-memory wake as proof.
type PermissionAuthoringConvergenceStore interface {
	PermissionAuthoringConvergence
	ClaimPermissionAuthoringConvergence(
		ctx context.Context,
		leaseOwner string,
		leaseDuration time.Duration,
		limit int,
	) ([]PermissionAuthoringConvergenceWork, error)
	// Acknowledge deletes successfully delivered work under its live lease.
	// Delivery history belongs to the aggregate audit, not this work queue.
	AcknowledgePermissionAuthoringConvergence(ctx context.Context, commandRowID, leaseOwner string) error
	RetryPermissionAuthoringConvergence(
		ctx context.Context,
		commandRowID string,
		leaseOwner string,
		lastError string,
		delay time.Duration,
	) error
}

type PermissionAuthoringAggregateRole string

const (
	PermissionAuthoringProfileBaseline       PermissionAuthoringAggregateRole = "PROFILE_BASELINE"
	PermissionAuthoringAdditivePermissionSet PermissionAuthoringAggregateRole = "ADDITIVE_PERMISSION_SET"
)

type LockedPermissionAuthoringAggregate struct {
	CanonicalPermissionSetID string
	Role                     PermissionAuthoringAggregateRole
	Revision                 uint64
}

// PermissionAuthoringTargetStore resolves the caller-selected identity and
// owns the physical root/catalog lock order plus the canonical PermissionSet
// role and revision. Resolve runs only for a newly accepted, unbound command,
// locks the mutable mapping it reads, and enforces the disjoint target domains:
// Profile maps to a Profile baseline; PermissionSet maps to an additive root.
// Semantic catalog reads remain on their existing repository ports.
type PermissionAuthoringTargetStore interface {
	ResolvePermissionAuthoringTarget(ctx context.Context, targetKind, targetID string) (permissionSetID string, found bool, err error)
	LockPermissionSetAuthoring(ctx context.Context, permissionSetID string) (aggregate LockedPermissionAuthoringAggregate, found bool, err error)
	// LockPermissionAuthoringCustomPermissions acquires the request-bounded
	// catalog locks before the Permission State authorization fence. This is a
	// physical ordering primitive, not a second catalog read authority.
	LockPermissionAuthoringCustomPermissions(ctx context.Context, customPermissionIDs []string) error
	AdvancePermissionSetAuthoringRevision(ctx context.Context, permissionSetID string, expected uint64) (uint64, error)
}

// PermissionAuthoringRecordMutationPlan is a Records-owned, validation-
// complete ObjectPermissions/FieldPermissions lifecycle plan. Apply must join
// the caller's existing pessimistic transaction and must not own commit,
// Permission State finalization, or cache/projection convergence.
type PermissionAuthoringRecordMutationPlan interface {
	Changed() bool
	// FinalChanges returns the immutable logical Object/Field delta derived
	// from Records' final post-lifecycle images. Callers must not audit or
	// advance aggregate state from the original request.
	FinalChanges() permissionauthoringv1.Changes
	Apply(ctx context.Context) error
}

type PermissionAuthoringObjectGrant struct {
	ID           string
	ResourceType string
	Deleted      bool
	Read         bool
	Create       bool
	Edit         bool
	Delete       bool
	ViewAll      bool
	ModifyAll    bool
}

type PermissionAuthoringFieldGrant struct {
	ID           string
	ResourceType string
	FieldName    string
	Deleted      bool
	Read         bool
	Edit         bool
}

// PermissionAuthoringGrantReader projects retained physical CRM permission
// rows into neutral resource identities before they reach the aggregate or
// Records authoring port.
type PermissionAuthoringGrantReader interface {
	ListPermissionAuthoringObjectGrants(ctx context.Context, permissionSetID string) ([]PermissionAuthoringObjectGrant, error)
	// LockPermissionAuthoringFieldGrants returns every retained physical row for
	// the PermissionSet under the caller's authoring transaction. Callers must
	// resolve simple and qualified Field aliases as one logical grant while the
	// complete physical alias set remains locked.
	LockPermissionAuthoringFieldGrants(ctx context.Context, permissionSetID string) ([]PermissionAuthoringFieldGrant, error)
}

// PermissionAuthoringRecordMutationPlanner preserves Records as the lifecycle
// owner for ObjectPermissions and FieldPermissions while allowing the
// Permissions aggregate to own the outer five-family command.
type PermissionAuthoringRecordMutationPlanner interface {
	PlanPermissionAuthoringRecordMutation(
		ctx context.Context,
		permissionSetID string,
		objectChanges []permissionauthoringv1.ObjectGrantChange,
		fieldChanges []permissionauthoringv1.FieldGrantChange,
		actor *models.UserSession,
	) (PermissionAuthoringRecordMutationPlan, error)
}
