/*
Copyright AppsCode Inc. and Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

import (
	core "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kmapi "kmodules.xyz/client-go/api/v1"
	mona "kmodules.xyz/monitoring-agent-api/api/v1"
	ofstv1 "kmodules.xyz/offshoot-api/api/v1"
	ofstv2 "kmodules.xyz/offshoot-api/api/v2"
)

const (
	ResourceCodePostgres     = "pg"
	ResourceKindPostgres     = "Postgres"
	ResourceSingularPostgres = "postgres"
	ResourcePluralPostgres   = "postgreses"
)

// Postgres defines a Postgres database.

// +genclient
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// +kubebuilder:object:root=true
// +kubebuilder:resource:path=postgreses,singular=postgres,shortName=pg,categories={datastore,kubedb,appscode,all}
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
type Postgres struct {
	metav1.TypeMeta   `json:",inline,omitempty"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Spec              PostgresSpec   `json:"spec,omitempty"`
	Status            PostgresStatus `json:"status,omitempty"`
}
type PostgreSQLMode string

const (
	PostgreSQLModeStandAlone    PostgreSQLMode = "Standalone"
	PostgreSQLModeRemoteReplica PostgreSQLMode = "RemoteReplica"
	PostgreSQLModeCluster       PostgreSQLMode = "Cluster"
)

type PostgresSpec struct {
	// AutoOps contains configuration of automatic ops-request-recommendation generation
	// +optional
	AutoOps AutoOpsSpec `json:"autoOps,omitempty"`

	// Distributed if set true, manifestwork objects will be created instead of raw resources
	Distributed bool `json:"distributed,omitempty"`

	// Version of Postgres to be deployed.
	Version string `json:"version"`

	// Number of instances to deploy for a Postgres database.
	Replicas *int32 `json:"replicas,omitempty"`

	// Standby mode
	StandbyMode *PostgresStandbyMode `json:"standbyMode,omitempty"`

	// Streaming mode
	StreamingMode *PostgresStreamingMode `json:"streamingMode,omitempty"`

	// SynchronousReplicationConfig holds fine-grained config for synchronous replication.
	// Only applicable when StreamingMode is Synchronous.
	// +optional
	SynchronousReplicationConfig *PostgresSynchronousReplicationSpec `json:"synchronousReplicationConfig,omitempty"`

	// + optional
	Mode *PostgreSQLMode `json:"mode,omitempty"`
	// RemoteReplica implies that the instance will be a MySQL Read Only Replica,
	// and it will take reference of  appbinding of the source
	// +optional
	RemoteReplica *RemoteReplicaSpec `json:"remoteReplica,omitempty"`

	// Leader election configuration
	// +optional
	LeaderElection *PostgreLeaderElectionConfig `json:"leaderElection,omitempty"`

	// Database authentication secret
	// +optional
	AuthSecret *SecretReference `json:"authSecret,omitempty"`

	// StorageType can be durable (default) or ephemeral
	StorageType StorageType `json:"storageType,omitempty"`

	// Storage to specify how storage shall be used.
	Storage *core.PersistentVolumeClaimSpec `json:"storage,omitempty"`

	// ClientAuthMode for sidecar or sharding. (default will be md5. [md5;scram;cert])
	ClientAuthMode PostgresClientAuthMode `json:"clientAuthMode,omitempty"`

	// SSLMode for both standalone and clusters. [disable;allow;prefer;require;verify-ca;verify-full]
	SSLMode PostgresSSLMode `json:"sslMode,omitempty"`

	// Init is used to initialize database
	// +optional
	Init *InitSpec `json:"init,omitempty"`

	// Monitor is used monitor database instance
	// +optional
	Monitor *mona.AgentSpec `json:"monitor,omitempty"`

	// ConfigSecret is an optional field to provide custom configuration file for database (i.e postgresql.conf).
	// If specified, this file will be used as configuration file otherwise default configuration file will be used.
	ConfigSecret *core.LocalObjectReference `json:"configSecret,omitempty"`

	Configuration *PostgresConfiguration `json:"configuration,omitempty"`

	// PodTemplate is an optional configuration for pods used to expose database
	// +optional
	PodTemplate ofstv2.PodTemplateSpec `json:"podTemplate,omitempty"`

	// ServiceTemplates is an optional configuration for services used to expose database
	// +optional
	ServiceTemplates []NamedServiceTemplateSpec `json:"serviceTemplates,omitempty"`

	// TLS contains tls configurations for client and server.
	// +optional
	TLS *kmapi.TLSConfig `json:"tls,omitempty"`

	// Indicates that the database is halted and all offshoot Kubernetes resources except PVCs are deleted.
	// +optional
	Halted bool `json:"halted,omitempty"`

	// DeletionPolicy controls the delete operation for database
	// +optional
	DeletionPolicy DeletionPolicy `json:"deletionPolicy,omitempty"`

	// EnforceFsGroup Is Used when the storageClass's CSI Driver doesn't support FsGroup properties properly.
	// If It's true then The Init Container will run as RootUser and
	// the init-container will set user's permission for the mounted pvc volume with which coordinator and postgres containers are going to run.
	// In postgres it is /var/pv
	// +optional
	EnforceFsGroup bool `json:"enforceFsGroup,omitempty"`

	// AllowedSchemas defines the types of database schemas that MAY refer to
	// a database instance and the trusted namespaces where those schema resources MAY be
	// present.
	//
	// +kubebuilder:default={namespaces:{from: Same}}
	// +optional
	AllowedSchemas *AllowedConsumers `json:"allowedSchemas,omitempty"`

	// HealthChecker defines attributes of the health checker
	// +optional
	// +kubebuilder:default={periodSeconds: 10, timeoutSeconds: 10, failureThreshold: 1}
	HealthChecker kmapi.HealthCheckSpec `json:"healthChecker"`

	// Archiver controls database backup using Archiver CR
	// +optional
	Archiver *Archiver `json:"archiver,omitempty"`

	// Arbiter controls spec for arbiter pods
	// +optional
	Arbiter *ArbiterSpec `json:"arbiter,omitempty"`

	// +optional
	Replication *PostgresReplication `json:"replication,omitempty"`

	// +optional
	ReadReplicas []ReadReplicaSpec `json:"readReplicas,omitempty"`

	// TDE configures Transparent Data Encryption (encryption at rest) using the
	// Percona pg_tde extension. It is only valid when the referenced
	// PostgresVersion has spec.tde.supported = true (a Percona distribution).
	// +optional
	TDE *PostgresTDESpec `json:"tde,omitempty"`
}

// PostgresTDESpec configures Transparent Data Encryption (encryption at rest)
// for a Postgres instance using the Percona pg_tde extension.
type PostgresTDESpec struct {
	// KeyProvider selects and configures the KMS that backs the principal key.
	KeyProvider TDEKeyProvider `json:"keyProvider"`

	// EncryptWAL turns on cluster-wide WAL encryption (pg_tde.wal_encrypt=on).
	// It requires a global provider (Vault or KMIP) and a rolling restart.
	// +optional
	EncryptWAL bool `json:"encryptWAL,omitempty"`

	// EnforceEncryption sets pg_tde.enforce_encryption=on so that no unencrypted
	// table can be created.
	// +optional
	EnforceEncryption bool `json:"enforceEncryption,omitempty"`

	// DefaultEncryptedTables makes tde_heap the default table access method
	// (default_table_access_method=tde_heap) so all new tables are encrypted.
	// +optional
	DefaultEncryptedTables bool `json:"defaultEncryptedTables,omitempty"`

	// Cipher selects the encryption algorithm. One of aes_128 (default) or aes_256.
	// +kubebuilder:validation:Enum=aes_128;aes_256
	// +optional
	Cipher string `json:"cipher,omitempty"`
}

// TDEKeyProvider selects exactly one KMS backend for the principal key.
type TDEKeyProvider struct {
	// Vault configures a HashiCorp Vault (KVv2) global key provider.
	// +optional
	Vault *TDEVaultProvider `json:"vault,omitempty"`

	// KMIP configures a KMIP server global key provider.
	// +optional
	KMIP *TDEKMIPProvider `json:"kmip,omitempty"`

	// File configures a local keyring file provider. Discouraged: it is only
	// valid for a single-node (standalone) Postgres and cannot back WAL encryption.
	// +optional
	File *TDEFileProvider `json:"file,omitempty"`
}

// TDEVaultProvider configures a HashiCorp Vault KVv2 global key provider.
type TDEVaultProvider struct {
	// Address is the Vault server URL, e.g. https://vault.example.com:8200.
	Address string `json:"address"`

	// MountPath is the KVv2 secrets engine mount, e.g. "secret".
	MountPath string `json:"mountPath"`

	// TokenSecretRef references a Secret holding the Vault token under the key
	// "token". It is mounted outside PGDATA at an identical path on every pod.
	TokenSecretRef core.LocalObjectReference `json:"tokenSecretRef"`

	// CASecretRef optionally references a Secret holding the CA bundle used to
	// verify Vault's TLS certificate under the key "ca.crt".
	// +optional
	CASecretRef *core.LocalObjectReference `json:"caSecretRef,omitempty"`

	// Namespace is an optional Vault Enterprise namespace.
	// +optional
	Namespace string `json:"namespace,omitempty"`
}

// TDEKMIPProvider configures a KMIP server global key provider.
type TDEKMIPProvider struct {
	// Address is the KMIP server host.
	Address string `json:"address"`

	// Port is the KMIP server port.
	// +kubebuilder:validation:Minimum=1
	// +kubebuilder:validation:Maximum=65535
	Port int32 `json:"port"`

	// CredentialSecretRef references a Secret holding the KMIP client credentials
	// under the keys "ca.crt", "client.crt" and "client.key". It is mounted
	// outside PGDATA at an identical path on every pod.
	CredentialSecretRef core.LocalObjectReference `json:"credentialSecretRef"`
}

// TDEFileProvider configures a local keyring file provider (standalone only).
type TDEFileProvider struct {
	// Path is the keyring file path on a mounted volume outside PGDATA.
	Path string `json:"path"`
}

type PostgresConfiguration struct {
	ConfigurationSpec `json:",inline,omitempty"`
	// +optional
	Tuning *PostgresTuningConfig `json:"tuning,omitempty"`
}

type WALLimitPolicy string

const (
	WALKeepSize     WALLimitPolicy = "WALKeepSize"
	ReplicationSlot WALLimitPolicy = "ReplicationSlot"
	WALKeepSegment  WALLimitPolicy = "WALKeepSegment"
)

type PostgresReplication struct {
	// WALimitPolicy defines which WAL retention policy to use.
	WALLimitPolicy WALLimitPolicy `json:"walLimitPolicy"`

	// +optional
	WalKeepSizeInMegaBytes *int32 `json:"walKeepSize,omitempty"`
	// +optional
	WalKeepSegment *int32 `json:"walKeepSegment,omitempty"`
	// +optional
	MaxSlotWALKeepSizeInMegaBytes *int32 `json:"maxSlotWALKeepSize,omitempty"`

	// ForceFailoverAcceptingDataLossAfter is the maximum time to wait before running a force failover process
	// This is helpful for a scenario where the old primary is not available and it has the most updated wal lsn
	// Doing force failover may or may not end up loosing data depending on any wrtie transaction
	// in the range lagged lsn between the new primary and the old primary
	// +optional
	ForceFailoverAcceptingDataLossAfter *metav1.Duration `json:"forceFailoverAcceptingDataLossAfter,omitempty"`
}

type ArbiterSpec struct {
	// Compute Resources required by the sidecar container.
	// +optional
	Resources core.ResourceRequirements `json:"resources,omitempty"`
	// NodeSelector is a selector which must be true for the pod to fit on a node.
	// Selector which must match a node's labels for the pod to be scheduled on that node.
	// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
	// +optional
	// +mapType=atomic
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`
	// If specified, the pod's tolerations.
	// +optional
	Tolerations []core.Toleration `json:"tolerations,omitempty"`
}

type ReadReplicaSpec struct {
	// Name specifies the name of the read replica
	Name string `json:"name"`
	// Number of instances to deploy for a Postgres database.
	Replicas *int32 `json:"replicas,omitempty"`
	// Compute Resources required by the sidecar container.
	// +optional
	Resources core.ResourceRequirements `json:"resources,omitempty"`
	// NodeSelector is a selector which must be true for the pod to fit on a node.
	// Selector which must match a node's labels for the pod to be scheduled on that node.
	// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
	// +optional
	// +mapType=atomic
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`
	// If specified, the pod's tolerations.
	// +optional
	Tolerations []core.Toleration `json:"tolerations,omitempty"`
	// StorageType can be durable (default) or ephemeral
	StorageType StorageType `json:"storageType,omitempty"`
	// Storage to specify how storage shall be used.
	Storage *core.PersistentVolumeClaimSpec `json:"storage,omitempty"`
	// PodPlacementPolicy is the reference of the podPlacementPolicy
	// +kubebuilder:default={name:"default"}
	// +optional
	PodPlacementPolicy *core.LocalObjectReference `json:"podPlacementPolicy,omitempty"`
	// ServiceTemplate is an optional configuration for services used to expose database
	// +optional
	ServiceTemplate *ofstv1.ServiceTemplateSpec `json:"serviceTemplate,omitempty"`
}

// PostgreLeaderElectionConfig contains essential attributes of leader election.
type PostgreLeaderElectionConfig struct {
	// LeaseDuration is the duration in second that non-leader candidates will
	// wait to force acquire leadership. This is measured against time of
	// last observed ack. Default 15
	// Deprecated
	LeaseDurationSeconds int32 `json:"leaseDurationSeconds,omitempty"`
	// RenewDeadline is the duration in second that the acting master will retry
	// refreshing leadership before giving up. Normally, LeaseDuration * 2 / 3.
	// Default 10
	// Deprecated
	RenewDeadlineSeconds int32 `json:"renewDeadlineSeconds,omitempty"`
	// RetryPeriod is the duration in second the LeaderElector clients should wait
	// between tries of actions. Normally, LeaseDuration / 3.
	// Default 2
	// Deprecated
	RetryPeriodSeconds int32 `json:"retryPeriodSeconds,omitempty"`

	// MaximumLagBeforeFailover is used as maximum lag tolerance for the cluster.
	// when ever a replica is lagging more than MaximumLagBeforeFailover
	// this node need to sync manually with the primary node. default value is 32MB
	// +default=33554432
	// +kubebuilder:default=33554432
	// +optional
	MaximumLagBeforeFailover uint64 `json:"maximumLagBeforeFailover,omitempty"`

	// Period between Node.Tick invocations
	// +kubebuilder:default="100ms"
	// +optional
	Period metav1.Duration `json:"period,omitempty"`

	// ElectionTick is the number of Node.Tick invocations that must pass between
	//	elections. That is, if a follower does not receive any message from the
	//  leader of current term before ElectionTick has elapsed, it will become
	//	candidate and start an election. ElectionTick must be greater than
	//  HeartbeatTick. We suggest ElectionTick = 10 * HeartbeatTick to avoid
	//  unnecessary leader switching. default value is 10.
	// +default=10
	// +kubebuilder:default=10
	// +optional
	ElectionTick int32 `json:"electionTick,omitempty"`

	// HeartbeatTick is the number of Node.Tick invocations that must pass between
	// heartbeats. That is, a leader sends heartbeat messages to maintain its
	// leadership every HeartbeatTick ticks. default value is 1.
	// +default=1
	// +kubebuilder:default=1
	// +optional
	HeartbeatTick int32 `json:"heartbeatTick,omitempty"`

	// TransferLeadershipInterval retry interval for transfer leadership
	// to the healthiest node
	// +kubebuilder:default="1s"
	// +optional
	TransferLeadershipInterval *metav1.Duration `json:"transferLeadershipInterval,omitempty"`

	// TransferLeadershipTimeout retry timeout for transfer leadership
	// to the healthiest node
	// +kubebuilder:default="60s"
	// +optional
	TransferLeadershipTimeout *metav1.Duration `json:"transferLeadershipTimeout,omitempty"`
}

// +kubebuilder:validation:Enum=server;archiver;metrics-exporter
type PostgresCertificateAlias string

const (
	PostgresServerCert          PostgresCertificateAlias = "server"
	PostgresClientCert          PostgresCertificateAlias = "client"
	PostgresArchiverCert        PostgresCertificateAlias = "archiver"
	PostgresGRPCCaCert          PostgresCertificateAlias = "grpc-ca"
	PostgresGRPCServerCert      PostgresCertificateAlias = "grpc-server"
	PostgresGRPCClientCert      PostgresCertificateAlias = "grpc-client"
	PostgresMetricsExporterCert PostgresCertificateAlias = "metrics-exporter"
)

type PostgresStatus struct {
	// Specifies the current phase of the database
	// +optional
	Phase DatabasePhase `json:"phase,omitempty"`
	// observedGeneration is the most recent generation observed for this resource. It corresponds to the
	// resource's generation, which is updated on mutation by the API Server.
	// +optional
	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
	// Conditions applied to the database, such as approval or denial.
	// +optional
	Conditions []kmapi.Condition `json:"conditions,omitempty"`
	// +optional
	AuthSecret *Age `json:"authSecret,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type PostgresList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	// Items is a list of Postgres CRD objects
	Items []Postgres `json:"items,omitempty"`
}

type RecoveryTarget struct {
	// TargetTime specifies the time stamp up to which recovery will proceed.
	TargetTime string `json:"targetTime,omitempty"`
	// TargetTimeline specifies recovering into a particular timeline.
	// The default is to recover along the same timeline that was current when the base backup was taken.
	TargetTimeline string `json:"targetTimeline,omitempty"`
	// TargetXID specifies the transaction ID up to which recovery will proceed.
	TargetXID string `json:"targetXID,omitempty"`
	// TargetInclusive specifies whether to include ongoing transaction in given target point.
	TargetInclusive *bool `json:"targetInclusive,omitempty"`
}

// +kubebuilder:validation:Enum=Hot;Warm
type PostgresStandbyMode string

const (
	HotPostgresStandbyMode  PostgresStandbyMode = "Hot"
	WarmPostgresStandbyMode PostgresStandbyMode = "Warm"
)

// +kubebuilder:validation:Enum=Synchronous;Asynchronous
type PostgresStreamingMode string

const (
	SynchronousPostgresStreamingMode  PostgresStreamingMode = "Synchronous"
	AsynchronousPostgresStreamingMode PostgresStreamingMode = "Asynchronous"
)

// PostgresSyncReplicationMode defines how standby replicas are selected for synchronous replication.
// +kubebuilder:validation:Enum=Any;First
type PostgresSyncReplicationMode string

const (
	// PostgresSyncReplicationModeAny uses quorum-based selection: wait for any NumSyncReplicas standbys.
	PostgresSyncReplicationModeAny PostgresSyncReplicationMode = "Any"
	// PostgresSyncReplicationModeFirst uses priority-based selection: wait for the first NumSyncReplicas standbys in list order.
	PostgresSyncReplicationModeFirst PostgresSyncReplicationMode = "First"
)

// PostgresSynchronousCommitLevel maps to PostgreSQL's synchronous_commit parameter.
// +kubebuilder:validation:Enum=On;RemoteApply;RemoteWrite;Local;Off
type PostgresSynchronousCommitLevel string

const (
	// PostgresSynchronousCommitOn waits until the standby has written and flushed WAL to disk.
	PostgresSynchronousCommitOn PostgresSynchronousCommitLevel = "On"
	// PostgresSynchronousCommitRemoteApply waits until the standby has applied the WAL.
	PostgresSynchronousCommitRemoteApply PostgresSynchronousCommitLevel = "RemoteApply"
	// PostgresSynchronousCommitRemoteWrite waits until the standby has written WAL to its OS buffer (default).
	PostgresSynchronousCommitRemoteWrite PostgresSynchronousCommitLevel = "RemoteWrite"
	// PostgresSynchronousCommitLocal waits for local WAL flush only; standby is not waited on.
	PostgresSynchronousCommitLocal PostgresSynchronousCommitLevel = "Local"
	// PostgresSynchronousCommitOff allows commit without waiting for WAL flush.
	PostgresSynchronousCommitOff PostgresSynchronousCommitLevel = "Off"
)

// PostgresSynchronousReplicationSpec configures fine-grained synchronous replication behavior.
// Only applicable when spec.streamingMode is Synchronous.
//
// Sample configurations:
//
//	# Case 1 — Minimal: all defaults (Any 1, RemoteWrite, auto-generated pod list)
//	streamingMode: Synchronous
//	# synchronousReplicationConfig omitted → ANY 1 ("pg-0","pg-1","pg-2")
//
//	# Case 2 — Quorum: wait for any 2 of N standbys
//	synchronousReplicationConfig:
//	  mode: Any
//	  numSyncReplicas: 2
//	  commitLevel: RemoteWrite
//
//	# Case 3 — Priority: ordered list, first live standby wins
//	synchronousReplicationConfig:
//	  mode: First
//	  numSyncReplicas: 1
//	  commitLevel: On
//
//	# Case 4 — Explicit standby names with Any mode
//	synchronousReplicationConfig:
//	  mode: Any
//	  numSyncReplicas: 2
//	  standbyNames: [pg-1, pg-2, pg-3]
//	  commitLevel: RemoteWrite
//
//	# Case 5 — Explicit standby names with First mode (order = priority)
//	synchronousReplicationConfig:
//	  mode: First
//	  numSyncReplicas: 1
//	  standbyNames: [pg-3, pg-1, pg-2]   # pg-3 has highest priority
//	  commitLevel: RemoteApply
//
//	# Case 6 — Strongest durability: WAL flushed on standby before commit returns
//	synchronousReplicationConfig:
//	  mode: Any
//	  numSyncReplicas: 1
//	  commitLevel: On
//
//	# Case 7 — Relaxed durability: only local WAL flush, standby not waited on
//	synchronousReplicationConfig:
//	  mode: Any
//	  numSyncReplicas: 1
//	  commitLevel: Local
//
//	# Case 8 — No WAL flush guarantee at all
//	synchronousReplicationConfig:
//	  mode: Any
//	  numSyncReplicas: 1
//	  commitLevel: Off
//
//	# Case 9 — Wildcard: accept any connected standby (useful when standby
//	#           application_names are unknown, e.g. external DR replicas).
//	#           Mutually exclusive with standbyNames.
//	#           With First mode, avoid wildcard — priority order is non-deterministic.
//	synchronousReplicationConfig:
//	  mode: Any
//	  numSyncReplicas: 1
//	  useWildcard: true
//	  # renders: ANY 1 (*)
//
//	# Case 10 — Wildcard quorum: any 2 standbys regardless of name
//	synchronousReplicationConfig:
//	  mode: Any
//	  numSyncReplicas: 2
//	  useWildcard: true
//	  # renders: ANY 2 (*)
type PostgresSynchronousReplicationSpec struct {
	// Mode controls how standbys are selected: Any (quorum) or First (priority).
	// Defaults to Any.
	// +optional
	Mode *PostgresSyncReplicationMode `json:"mode,omitempty"`

	// NumSyncReplicas is the number of synchronous standby replicas to wait for.
	// Must be >= 1 and less than spec.replicas.
	// Defaults to 1.
	// +optional
	NumSyncReplicas *int32 `json:"numSyncReplicas,omitempty"`

	// CommitLevel maps to PostgreSQL's synchronous_commit parameter, controlling the
	// durability vs. performance trade-off for synchronous standbys.
	// Defaults to RemoteWrite.
	// +optional
	CommitLevel *PostgresSynchronousCommitLevel `json:"commitLevel,omitempty"`

	// StandbyNames is an explicit ordered list of standby application_names to include in
	// synchronous_standby_names. When set, only these names participate in synchronous
	// replication instead of the auto-generated list of all pod names.
	// For FIRST mode the order determines priority (first entry = highest priority).
	// Must not contain duplicates or empty strings.
	// When absent, all standby pods are included in ascending pod-index order.
	// Mutually exclusive with UseWildcard.
	// +optional
	// +listType=atomic
	StandbyNames []string `json:"standbyNames,omitempty"`

	// UseWildcard, when true, uses '*' in synchronous_standby_names to match any
	// connected standby regardless of its application_name. Useful when standby names
	// are not known in advance (e.g. external DR replicas with custom application_name).
	// Avoid combining with mode: First — connection-order priority is non-deterministic.
	// Mutually exclusive with StandbyNames.
	// +optional
	UseWildcard *bool `json:"useWildcard,omitempty"`
}

// ref: https://www.postgresql.org/docs/13/libpq-ssl.html
// +kubebuilder:validation:Enum=disable;allow;prefer;require;verify-ca;verify-full
type PostgresSSLMode string

const (
	// PostgresSSLModeDisable represents `disable` sslMode. It ensures that the server does not use TLS/SSL.
	PostgresSSLModeDisable PostgresSSLMode = "disable"

	// PostgresSSLModeAllow represents `allow` sslMode. 	I don't care about security,
	// but I will pay the overhead of encryption if the server insists on it.
	PostgresSSLModeAllow PostgresSSLMode = "allow"

	// PostgresSSLModePrefer represents `preferSSL` sslMode.
	// I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it.
	PostgresSSLModePrefer PostgresSSLMode = "prefer"

	// PostgresSSLModeRequire represents `requiteSSL` sslmode. I want my data to be encrypted, and I accept the overhead.
	// I trust that the network will make sure I always connect to the server I want.
	PostgresSSLModeRequire PostgresSSLMode = "require"

	// PostgresSSLModeVerifyCA represents `verify-ca` sslmode. I want my data encrypted, and I accept the overhead.
	// I want to be sure that I connect to a server that I trust.
	PostgresSSLModeVerifyCA PostgresSSLMode = "verify-ca"

	// PostgresSSLModeVerifyFull represents `verify-full` sslmode. I want my data encrypted, and I accept the overhead.
	// I want to be sure that I connect to a server I trust, and that it's the one I specify.
	PostgresSSLModeVerifyFull PostgresSSLMode = "verify-full"
)

// PostgresClientAuthMode represents the ClientAuthMode of PostgreSQL clusters ( replicaset )
// ref: https://www.postgresql.org/docs/12/auth-methods.html
// +kubebuilder:validation:Enum=md5;scram;cert
type PostgresClientAuthMode string

const (
	// ClientAuthModeMD5 uses a custom less secure challenge-response mechanism.
	// It prevents password sniffing and avoids storing passwords on the server in plain text but provides no protection
	// if an attacker manages to steal the password hash from the server.
	// Also, the MD5 hash algorithm is nowadays no longer considered secure against determined attacks
	ClientAuthModeMD5 PostgresClientAuthMode = "md5"

	// ClientAuthModeScram performs SCRAM-SHA-256 authentication, as described in RFC 7677.
	// It is a challenge-response scheme that prevents password sniffing on untrusted connections
	// and supports storing passwords on the server in a cryptographically hashed form that is thought to be secure.
	// This is the most secure of the currently provided methods, but it is not supported by older client libraries.
	ClientAuthModeScram PostgresClientAuthMode = "scram"

	// ClientAuthModeCert represents `cert clientcert=1` auth mode where client need to provide cert and private key for authentication.
	// When server is config with this auth method. Client can't connect with postgreSQL server with password. They need
	// to Send the client cert and client key certificate for authentication.
	ClientAuthModeCert PostgresClientAuthMode = "cert"
)

// PostgresTuningConfig defines configuration for PostgreSQL performance tuning
type PostgresTuningConfig struct {
	// Profile defines a predefined tuning profile for different workload types.
	// If specified, other tuning parameters will be calculated based on this profile.
	// +optional
	Profile *PostgresProfile `json:"profile,omitempty"`

	// MaxConnections defines the maximum number of concurrent connections.
	// If not specified, it will be calculated based on available memory and tuning profile.
	// +optional
	MaxConnections *int32 `json:"maxConnections,omitempty"`

	// StorageType defines the type of storage for tuning purposes.
	// If not specified, it will be inferred from StorageClass or default to HDD.
	// +optional
	StorageType *PostgresStorageType `json:"storageType,omitempty"`

	// DisableAutoTune disables automatic tuning entirely.
	// If set to true, no tuning will be applied.
	// +optional
	DisableAutoTune bool `json:"disableAutoTune,omitempty"`
}

// PostgresProfile defines predefined tuning profiles
// +kubebuilder:validation:Enum=web;oltp;dw;mixed;desktop
type PostgresProfile string

const (
	// PostgresTuningProfileWeb optimizes for web applications with many simple queries
	PostgresTuningProfileWeb PostgresProfile = "web"

	// PostgresTuningProfileOLTP optimizes for OLTP workloads with many short transactions
	PostgresTuningProfileOLTP PostgresProfile = "oltp"

	// PostgresTuningProfileDW optimizes for data warehousing with complex analytical queries
	PostgresTuningProfileDW PostgresProfile = "dw"

	// PostgresTuningProfileMixed optimizes for mixed workloads
	PostgresTuningProfileMixed PostgresProfile = "mixed"

	// PostgresTuningProfileDesktop optimizes for desktop or development environments
	PostgresTuningProfileDesktop PostgresProfile = "desktop"
)

// PostgresStorageType defines storage types for tuning purposes
// +kubebuilder:validation:Enum=ssd;hdd;san
type PostgresStorageType string

const (
	PostgresStorageTypeSSD PostgresStorageType = "ssd"
	PostgresStorageTypeHDD PostgresStorageType = "hdd"
	PostgresStorageTypeSAN PostgresStorageType = "san"
)

var _ Accessor = &Postgres{}

func (m *Postgres) GetObjectMeta() metav1.ObjectMeta {
	return m.ObjectMeta
}

func (m *Postgres) GetConditions() []kmapi.Condition {
	return m.Status.Conditions
}

func (m *Postgres) SetCondition(cond kmapi.Condition) {
	m.Status.Conditions = setCondition(m.Status.Conditions, cond)
}

func (m *Postgres) RemoveCondition(typ string) {
	m.Status.Conditions = removeCondition(m.Status.Conditions, typ)
}
