// Package events provides the NATS JetStream client, stream initialisation,
// typed event payloads, and a typed publish helper for all GPUaaS domain events.
//
// Event envelope format:
//
//	{ "event_id": "uuid", "event_type": "domain.name", "occurred_at": "RFC3339",
//	  "version": "1.0", "correlation_id": "...", "payload": {} }
//
// All subjects follow the stream bindings in doc/architecture/NATS_Stream_Config.md.
package events

import "time"

// Subject constants — one per event type. Use these everywhere; never use
// raw strings so that a subject rename is a single-location change.
const (
	SubjectDLQPrefix                        = "dlq."
	SubjectProvisioningRequested            = "gpuaas.provisioning.requested"
	SubjectProvisioningActive               = "gpuaas.provisioning.active"
	SubjectProvisioningFailed               = "gpuaas.provisioning.failed"
	SubjectProvisioningReleasingRequested   = "gpuaas.provisioning.releasing.requested"
	SubjectProvisioningReleasingCompleted   = "gpuaas.provisioning.releasing.completed"
	SubjectProvisioningReleaseFailed        = "gpuaas.provisioning.release_failed"
	SubjectProvisioningForceRelease         = "gpuaas.provisioning.force_release_requested"
	SubjectBillingLowBalanceWarning         = "platform.billing.low_balance_warning"
	SubjectBillingAutoReleasePending        = "platform.billing.auto_release_pending"
	SubjectBillingBalanceDepleted           = "platform.billing.balance_depleted"
	SubjectBillingBudgetThresholdCrossed    = "platform.billing.budget_threshold_crossed"
	SubjectBillingUsageMetered              = "platform.billing.usage.metered"
	SubjectPaymentsBalanceCredited          = "platform.payments.balance_credited"
	SubjectPaymentsReconcileFailed          = "platform.payments.reconcile_failed"
	SubjectAppsEntitlementUpdated           = "appplatform.catalog.entitlement.updated"
	SubjectAppInstanceRequested             = "appplatform.runtime.instance.requested"
	SubjectAppInstanceRunning               = "appplatform.runtime.instance.running"
	SubjectAppInstanceFailed                = "appplatform.runtime.instance.failed"
	SubjectAppInstanceDeleting              = "appplatform.runtime.instance.deleting"
	SubjectAppInstanceDeleted               = "appplatform.runtime.instance.deleted"
	SubjectSharedAppRuntimeRequested        = "appplatform.runtime.shared_runtime.requested"
	SubjectSharedAppRuntimeRunning          = "appplatform.runtime.shared_runtime.running"
	SubjectSharedAppRuntimeFailed           = "appplatform.runtime.shared_runtime.failed"
	SubjectSharedAppRuntimeDeleting         = "appplatform.runtime.shared_runtime.deleting"
	SubjectSharedAppRuntimeDeleted          = "appplatform.runtime.shared_runtime.deleted"
	SubjectAppInstanceUpgradeRequested      = "appplatform.runtime.instance.upgrade_requested"
	SubjectAppInstanceRollbackRequested     = "appplatform.runtime.instance.rollback_requested"
	SubjectAppInstanceDecommissionRequested = "appplatform.runtime.instance.decommission_requested"
	SubjectAppInstanceStopRequested         = "appplatform.runtime.instance.stop_requested"
	SubjectAppInstanceStartRequested        = "appplatform.runtime.instance.start_requested"
	SubjectAppInstanceRestartRequested      = "appplatform.runtime.instance.restart_requested"
	SubjectAppArtifactRegistered            = "appplatform.artifact.registered"
	SubjectAppArtifactVerified              = "platform.artifact.verified"
	SubjectAppArtifactRevoked               = "platform.artifact.revoked"
	SubjectAppArtifactPromoted              = "appplatform.artifact.promoted"
	SubjectAppArtifactDeprecated            = "appplatform.artifact.deprecated"
	SubjectAppArtifactRetired               = "appplatform.artifact.retired"
	SubjectStorageAttachmentRequested       = "storage.attachment.requested"
	SubjectStorageAttachmentDetachRequested = "storage.attachment.detach_requested"
	SubjectStorageAttachmentMounted         = "storage.attachment.mounted"
	SubjectStorageAttachmentFailed          = "storage.attachment.failed"
	SubjectStorageAttachmentDetached        = "storage.attachment.detached"
	SubjectStorageAttachmentDetachFailed    = "storage.attachment.detach_failed"
)

// Envelope is the standard wrapper for every domain event published to NATS.
// Publish via [PublishTyped] which sets EventID, OccurredAt, and Version
// automatically.
type Envelope struct {
	EventID       string    `json:"event_id"`
	EventType     string    `json:"event_type"`
	OccurredAt    time.Time `json:"occurred_at"`
	Version       string    `json:"version"`
	CorrelationID string    `json:"correlation_id"`
	Payload       any       `json:"payload"`
}

// ---------------------------------------------------------------------------
// Typed payload structs — one per event subject.
// ---------------------------------------------------------------------------

// ProvisionRequestedPayload is the payload for gpuaas.provisioning.requested.
type ProvisionRequestedPayload struct {
	AllocationID  string `json:"allocation_id"`
	UserID        string `json:"user_id"`
	OrgID         string `json:"org_id,omitempty"`
	ProjectID     string `json:"project_id,omitempty"`
	SchedulerType string `json:"scheduler_type"`
	SKU           string `json:"sku"`
	GPUsTotal     int    `json:"gpus_total"`
	RegionCode    string `json:"region_code"`
}

// ProvisionActivePayload is the payload for gpuaas.provisioning.active.
type ProvisionActivePayload struct {
	AllocationID string `json:"allocation_id"`
	UserID       string `json:"user_id"`
	OrgID        string `json:"org_id,omitempty"`
	ProjectID    string `json:"project_id,omitempty"`
	NodeID       string `json:"node_id"`
	Host         string `json:"host"`
	Port         int    `json:"port"`
	SSHUsername  string `json:"ssh_username"`
	SKU          string `json:"sku"`
	GPUsTotal    int    `json:"gpus_total"`
	ActiveAt     string `json:"active_at"` // RFC3339
}

// ProvisionFailedPayload is the payload for gpuaas.provisioning.failed.
type ProvisionFailedPayload struct {
	AllocationID string `json:"allocation_id"`
	UserID       string `json:"user_id"`
	OrgID        string `json:"org_id,omitempty"`
	ProjectID    string `json:"project_id,omitempty"`
	Reason       string `json:"reason"`
}

// ReleasingRequestedPayload is the payload for gpuaas.provisioning.releasing.requested.
type ReleasingRequestedPayload struct {
	AllocationID string `json:"allocation_id"`
	UserID       string `json:"user_id"`
	OrgID        string `json:"org_id,omitempty"`
	ProjectID    string `json:"project_id,omitempty"`
	NodeID       string `json:"node_id"`
}

// ReleasingCompletedPayload is the payload for gpuaas.provisioning.releasing.completed.
type ReleasingCompletedPayload struct {
	AllocationID string `json:"allocation_id"`
	UserID       string `json:"user_id"`
	OrgID        string `json:"org_id,omitempty"`
	ProjectID    string `json:"project_id,omitempty"`
	NodeID       string `json:"node_id"`
	ReleasedAt   string `json:"released_at"` // RFC3339
}

// ReleaseFailedPayload is the payload for gpuaas.provisioning.release_failed.
// On receipt by billing-worker, the usage window is closed (no further charges).
// On receipt by notification-relay, a user notification is dispatched.
type ReleaseFailedPayload struct {
	AllocationID string `json:"allocation_id"`
	UserID       string `json:"user_id"`
	OrgID        string `json:"org_id,omitempty"`
	ProjectID    string `json:"project_id,omitempty"`
	NodeID       string `json:"node_id"`
	Reason       string `json:"reason"`
}

// ForceReleaseRequestedPayload is the payload for
// gpuaas.provisioning.force_release_requested. Produced by billing-worker or admin
// action; consumed by provisioning-worker.
type ForceReleaseRequestedPayload struct {
	AllocationID       string `json:"allocation_id"`
	UserID             string `json:"user_id"`
	OrgID              string `json:"org_id,omitempty"`
	ProjectID          string `json:"project_id,omitempty"`
	RequestedByAdminID string `json:"requested_by_admin_id"`
}

// LowBalanceWarningPayload is the payload for platform.billing.low_balance_warning.
type LowBalanceWarningPayload struct {
	UserID         string `json:"user_id"`
	OrgID          string `json:"org_id,omitempty"`
	BalanceMinor   int64  `json:"balance_minor"`
	ThresholdMinor int64  `json:"threshold_minor"`
	Currency       string `json:"currency"`
	// Optional projected depletion details.
	ProjectedDepletionAt    string  `json:"projected_depletion_at,omitempty"`    // RFC3339
	ProjectedHoursRemaining float64 `json:"projected_hours_remaining,omitempty"` // advisory estimate
}

// AutoReleasePendingPayload is the payload for platform.billing.auto_release_pending.
type AutoReleasePendingPayload struct {
	UserID                  string   `json:"user_id"`
	OrgID                   string   `json:"org_id,omitempty"`
	Currency                string   `json:"currency"`
	ProjectedDepletionAt    string   `json:"projected_depletion_at"` // RFC3339
	ProjectedHoursRemaining float64  `json:"projected_hours_remaining"`
	ActiveAllocationIDs     []string `json:"active_allocation_ids"`
}

// BalanceDepletedPayload is the payload for platform.billing.balance_depleted.
type BalanceDepletedPayload struct {
	UserID                  string   `json:"user_id"`
	OrgID                   string   `json:"org_id,omitempty"`
	BalanceMinor            int64    `json:"balance_minor"`
	Currency                string   `json:"currency"`
	ActiveAllocationIDs     []string `json:"active_allocation_ids,omitempty"`
	ActiveAppInstanceIDs    []string `json:"active_app_instance_ids,omitempty"`
	SelectedAction          string   `json:"selected_action,omitempty"`
	ProjectedDepletionAt    string   `json:"projected_depletion_at,omitempty"`    // RFC3339
	ProjectedHoursRemaining float64  `json:"projected_hours_remaining,omitempty"` // advisory estimate
}

// BudgetThresholdCrossedPayload is the payload for
// platform.billing.budget_threshold_crossed. It is notification-only; enforcement
// decisions remain separate policy decisions.
type BudgetThresholdCrossedPayload struct {
	OrgID            string   `json:"org_id"`
	ProjectID        string   `json:"project_id,omitempty"`
	BudgetPolicyID   string   `json:"budget_policy_id"`
	ThresholdKey     string   `json:"threshold_key"`
	ScopeType        string   `json:"scope_type"`
	SpentMinor       int64    `json:"spent_minor"`
	BudgetMinor      int64    `json:"budget_minor"`
	Currency         string   `json:"currency"`
	PercentUsed      int64    `json:"percent_used"`
	PeriodStart      string   `json:"period_start"`
	PeriodEnd        string   `json:"period_end"`
	RecipientUserIDs []string `json:"recipient_user_ids,omitempty"`
}

// UsageMeteredPayload is the payload for platform.billing.usage.metered. Producers emit
// one closed or incremental usage interval; billing consumes it idempotently
// into usage_records, rated_usage_lines, and ledger entries.
type UsageMeteredPayload struct {
	UsageEventID                string         `json:"usage_event_id"`
	UsageSource                 string         `json:"usage_source"`
	UsageUnit                   string         `json:"usage_unit"`
	OrgID                       string         `json:"org_id,omitempty"`
	DepartmentID                string         `json:"department_id,omitempty"`
	ProjectID                   string         `json:"project_id,omitempty"`
	BillingAccountID            string         `json:"billing_account_id,omitempty"`
	ActorType                   string         `json:"actor_type,omitempty"`
	ActorID                     string         `json:"actor_id,omitempty"`
	RequestedByUserID           string         `json:"requested_by_user_id,omitempty"`
	RequestedByServiceAccountID string         `json:"requested_by_service_account_id,omitempty"`
	CredentialID                string         `json:"credential_id,omitempty"`
	AllocationID                string         `json:"allocation_id,omitempty"`
	AppInstanceID               string         `json:"app_instance_id,omitempty"`
	ProductID                   string         `json:"product_id,omitempty"`
	ResourceType                string         `json:"resource_type,omitempty"`
	ResourceID                  string         `json:"resource_id,omitempty"`
	RegionCode                  string         `json:"region_code,omitempty"`
	SKU                         string         `json:"sku,omitempty"`
	QuantityMillis              int64          `json:"quantity_millis"`
	UnitCount                   int64          `json:"unit_count"`
	BillFrom                    string         `json:"bill_from"`
	BillTo                      string         `json:"bill_to"`
	Currency                    string         `json:"currency,omitempty"`
	PricingSource               string         `json:"pricing_source,omitempty"`
	PricingPlanID               string         `json:"pricing_plan_id,omitempty"`
	PricingPlanVersion          string         `json:"pricing_plan_version,omitempty"`
	RateCardID                  string         `json:"rate_card_id,omitempty"`
	PricingSnapshot             map[string]any `json:"pricing_snapshot,omitempty"`
	OperatingMode               string         `json:"operating_mode,omitempty"`
	ControlPlaneScope           string         `json:"control_plane_scope,omitempty"`
	RuntimeBackend              string         `json:"runtime_backend,omitempty"`
	ControlPlaneComponent       bool           `json:"control_plane_component,omitempty"`
	IdempotencyKey              string         `json:"idempotency_key"`
	RequestID                   string         `json:"request_id,omitempty"`
	SourceCorrelationID         string         `json:"source_correlation_id,omitempty"`
	ReconciliationStatus        string         `json:"reconciliation_status,omitempty"`
	Metadata                    map[string]any `json:"metadata,omitempty"`
}

// BalanceCreditedPayload is the payload for platform.payments.balance_credited.
type BalanceCreditedPayload struct {
	UserID           string `json:"user_id"`
	OrgID            string `json:"org_id,omitempty"`
	AmountMinor      int64  `json:"amount_minor"`
	Currency         string `json:"currency"`
	LedgerEntryID    string `json:"ledger_entry_id"`
	PaymentSessionID string `json:"payment_session_id"`
	StripeSessionID  string `json:"stripe_session_id"`
}

// ReconcileFailedPayload is the payload for platform.payments.reconcile_failed.
type ReconcileFailedPayload struct {
	UserID           string `json:"user_id"`
	OrgID            string `json:"org_id,omitempty"`
	PaymentSessionID string `json:"payment_session_id"`
	StripeSessionID  string `json:"stripe_session_id"`
	Reason           string `json:"reason"`
}

// AppEntitlementUpdatedPayload is the payload for appplatform.catalog.entitlement.updated.
type AppEntitlementUpdatedPayload struct {
	OrgID           string         `json:"org_id"`
	ProjectID       string         `json:"project_id"`
	AppSlug         string         `json:"app_slug"`
	Enabled         bool           `json:"enabled"`
	PolicyOverrides map[string]any `json:"policy_overrides"`
	UpdatedByUserID string         `json:"updated_by_user_id,omitempty"`
}

// AppInstanceRequestedPayload is the payload for appplatform.runtime.instance.requested.
type AppInstanceRequestedPayload struct {
	AppInstanceID               string `json:"app_instance_id"`
	AppArtifactID               string `json:"app_artifact_id,omitempty"`
	AppSlug                     string `json:"app_slug"`
	AppVersion                  string `json:"app_version"`
	OrgID                       string `json:"org_id"`
	ProjectID                   string `json:"project_id"`
	RequestedByUserID           string `json:"requested_by_user_id,omitempty"`
	RequestedByServiceAccountID string `json:"requested_by_service_account_id,omitempty"`
	OperatorServiceAccountID    string `json:"operator_service_account_id,omitempty"`
}

// AppInstanceRunningPayload is the payload for appplatform.runtime.instance.running.
type AppInstanceRunningPayload struct {
	AppInstanceID string `json:"app_instance_id"`
	AppSlug       string `json:"app_slug"`
	OrgID         string `json:"org_id"`
	ProjectID     string `json:"project_id"`
	Endpoint      string `json:"endpoint,omitempty"`
}

// AppInstanceFailedPayload is the payload for appplatform.runtime.instance.failed.
type AppInstanceFailedPayload struct {
	AppInstanceID     string `json:"app_instance_id"`
	AppSlug           string `json:"app_slug"`
	OperatingMode     string `json:"operating_mode,omitempty"`
	ControlPlaneScope string `json:"control_plane_scope,omitempty"`
	RuntimeBackend    string `json:"runtime_backend,omitempty"`
	OrgID             string `json:"org_id"`
	ProjectID         string `json:"project_id"`
	Reason            string `json:"reason"`
}

// AppInstanceDeletingPayload is the payload for appplatform.runtime.instance.deleting.
type AppInstanceDeletingPayload struct {
	AppInstanceID     string `json:"app_instance_id"`
	AppSlug           string `json:"app_slug"`
	OrgID             string `json:"org_id"`
	ProjectID         string `json:"project_id"`
	RequestedByUserID string `json:"requested_by_user_id"`
}

// StorageAttachmentRequestedPayload is the payload for
// storage.attachment.requested. The worker owns provider grants and node-agent
// mount execution; API handlers only persist the intent and enqueue this event.
type StorageAttachmentRequestedPayload struct {
	AttachmentID       string `json:"attachment_id"`
	OrgID              string `json:"org_id"`
	ProjectID          string `json:"project_id"`
	BucketID           string `json:"bucket_id"`
	AllocationID       string `json:"allocation_id,omitempty"`
	WorkloadInstanceID string `json:"workload_instance_id,omitempty"`
	NodeID             string `json:"node_id,omitempty"`
	MountPath          string `json:"mount_path"`
	AccessMode         string `json:"access_mode"`
	WritePolicy        string `json:"write_policy"`
	ProviderBackend    string `json:"provider_backend"`
	ProviderFilesystem string `json:"provider_filesystem,omitempty"`
	RequestedByUserID  string `json:"requested_by_user_id,omitempty"`
}

// StorageAttachmentStatePayload is used by storage attachment lifecycle events
// after the worker or node agent advances the persisted state.
type StorageAttachmentStatePayload struct {
	AttachmentID       string `json:"attachment_id"`
	OrgID              string `json:"org_id"`
	ProjectID          string `json:"project_id"`
	BucketID           string `json:"bucket_id"`
	AllocationID       string `json:"allocation_id,omitempty"`
	WorkloadInstanceID string `json:"workload_instance_id,omitempty"`
	NodeID             string `json:"node_id,omitempty"`
	State              string `json:"state"`
	FailureCode        string `json:"failure_code,omitempty"`
	FailureMessage     string `json:"failure_message,omitempty"`
}

// AppInstanceDeletedPayload is the payload for appplatform.runtime.instance.deleted.
type AppInstanceDeletedPayload struct {
	AppInstanceID string `json:"app_instance_id"`
	AppSlug       string `json:"app_slug"`
	OrgID         string `json:"org_id"`
	ProjectID     string `json:"project_id"`
	DeletedAt     string `json:"deleted_at"`
}

// SharedAppRuntimeRequestedPayload is the payload for appplatform.runtime.shared_runtime.requested.
type SharedAppRuntimeRequestedPayload struct {
	SharedRuntimeID     string `json:"shared_runtime_id"`
	AppSlug             string `json:"app_slug"`
	AppVersion          string `json:"app_version"`
	OperatingMode       string `json:"operating_mode"`
	ControlPlaneScope   string `json:"control_plane_scope"`
	RuntimeBackend      string `json:"runtime_backend"`
	OrgID               string `json:"org_id"`
	RequestedByUserID   string `json:"requested_by_user_id,omitempty"`
	OperatorIdentityRef string `json:"operator_identity_ref,omitempty"`
}

// SharedAppRuntimeRunningPayload is the payload for appplatform.runtime.shared_runtime.running.
type SharedAppRuntimeRunningPayload struct {
	SharedRuntimeID   string `json:"shared_runtime_id"`
	AppSlug           string `json:"app_slug"`
	OperatingMode     string `json:"operating_mode"`
	ControlPlaneScope string `json:"control_plane_scope"`
	RuntimeBackend    string `json:"runtime_backend"`
	OrgID             string `json:"org_id"`
	Endpoint          string `json:"endpoint,omitempty"`
}

// SharedAppRuntimeFailedPayload is the payload for appplatform.runtime.shared_runtime.failed.
type SharedAppRuntimeFailedPayload struct {
	SharedRuntimeID string `json:"shared_runtime_id"`
	AppSlug         string `json:"app_slug"`
	OrgID           string `json:"org_id"`
	Reason          string `json:"reason"`
}

// SharedAppRuntimeDeletingPayload is the payload for appplatform.runtime.shared_runtime.deleting.
type SharedAppRuntimeDeletingPayload struct {
	SharedRuntimeID   string `json:"shared_runtime_id"`
	AppSlug           string `json:"app_slug"`
	RuntimeBackend    string `json:"runtime_backend"`
	OrgID             string `json:"org_id"`
	RequestedByUserID string `json:"requested_by_user_id,omitempty"`
}

// SharedAppRuntimeDeletedPayload is the payload for appplatform.runtime.shared_runtime.deleted.
type SharedAppRuntimeDeletedPayload struct {
	SharedRuntimeID string `json:"shared_runtime_id"`
	AppSlug         string `json:"app_slug"`
	OrgID           string `json:"org_id"`
	DeletedAt       string `json:"deleted_at"`
}

// AppInstanceUpgradeRequestedPayload is the payload for appplatform.runtime.instance.upgrade_requested.
type AppInstanceUpgradeRequestedPayload struct {
	AppInstanceID            string `json:"app_instance_id"`
	AppSlug                  string `json:"app_slug"`
	AppVersion               string `json:"app_version"`
	PreviousAppVersion       string `json:"previous_app_version"`
	OrgID                    string `json:"org_id"`
	ProjectID                string `json:"project_id"`
	RequestedByUserID        string `json:"requested_by_user_id"`
	OperatorServiceAccountID string `json:"operator_service_account_id,omitempty"`
}

// AppInstanceRollbackRequestedPayload is the payload for appplatform.runtime.instance.rollback_requested.
type AppInstanceRollbackRequestedPayload struct {
	AppInstanceID            string `json:"app_instance_id"`
	AppSlug                  string `json:"app_slug"`
	AppVersion               string `json:"app_version"`
	PreviousAppVersion       string `json:"previous_app_version"`
	OrgID                    string `json:"org_id"`
	ProjectID                string `json:"project_id"`
	RequestedByUserID        string `json:"requested_by_user_id"`
	OperatorServiceAccountID string `json:"operator_service_account_id,omitempty"`
}

// AppInstanceDecommissionRequestedPayload is the payload for appplatform.runtime.instance.decommission_requested.
type AppInstanceDecommissionRequestedPayload struct {
	AppInstanceID            string `json:"app_instance_id"`
	AppSlug                  string `json:"app_slug"`
	AppVersion               string `json:"app_version"`
	OrgID                    string `json:"org_id"`
	ProjectID                string `json:"project_id"`
	RequestedByUserID        string `json:"requested_by_user_id"`
	OperatorServiceAccountID string `json:"operator_service_account_id,omitempty"`
}

// AppInstanceLifecycleRequestedPayload is the payload for apps.instance stop/start/restart requests.
type AppInstanceLifecycleRequestedPayload struct {
	AppInstanceID               string `json:"app_instance_id"`
	AppSlug                     string `json:"app_slug"`
	AppVersion                  string `json:"app_version"`
	OrgID                       string `json:"org_id"`
	ProjectID                   string `json:"project_id"`
	RequestedByUserID           string `json:"requested_by_user_id"`
	RequestedByServiceAccountID string `json:"requested_by_service_account_id,omitempty"`
	OperatorServiceAccountID    string `json:"operator_service_account_id,omitempty"`
	Operation                   string `json:"operation"`
}

// AppArtifactLifecyclePayload is the payload for apps.artifact.* lifecycle events.
type AppArtifactLifecyclePayload struct {
	ArtifactID                   string `json:"artifact_id"`
	AppSlug                      string `json:"app_slug"`
	AppVersion                   string `json:"app_version"`
	ArtifactName                 string `json:"artifact_name"`
	ArtifactKind                 string `json:"artifact_kind"`
	SourceType                   string `json:"source_type"`
	SourceURI                    string `json:"source_uri,omitempty"`
	Repository                   string `json:"repository"`
	Digest                       string `json:"digest"`
	DigestAlgorithm              string `json:"digest_algorithm"`
	LifecycleState               string `json:"lifecycle_state"`
	TrustState                   string `json:"trust_state"`
	PromotedChannel              string `json:"promoted_channel,omitempty"`
	TargetEnvironment            string `json:"target_environment,omitempty"`
	OrgID                        string `json:"org_id"`
	ProjectID                    string `json:"project_id"`
	RequestedByUserID            string `json:"requested_by_user_id,omitempty"`
	RegisteredByServiceAccountID string `json:"registered_by_service_account_id,omitempty"`
}
