// Copyright 2024 RustFS Team
//
// 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.

use super::*;

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableBucketMarker {
    pub version: u16,
    pub catalog_type: &'static str,
    pub reserved_prefix: &'static str,
}

impl Default for TableBucketMarker {
    fn default() -> Self {
        Self {
            version: TABLE_BUCKET_CONFIG_VERSION,
            catalog_type: TABLE_BUCKET_CATALOG_TYPE,
            reserved_prefix: TABLE_RESERVED_PREFIX,
        }
    }
}

pub(crate) fn table_bucket_marker_json() -> Result<Vec<u8>, serde_json::Error> {
    serde_json::to_vec(&TableBucketMarker::default())
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogEntryState {
    Active,
    /// Persisted only behind a rename intent so older readers reject the unknown state and fail closed.
    Renaming,
    Deleting,
    Deleted,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableBucketEntry {
    pub version: u16,
    pub table_bucket: String,
    pub catalog_type: String,
    pub warehouse_root: String,
    pub state: TableCatalogEntryState,
    #[serde(default)]
    pub properties: BTreeMap<String, String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub active_rename_id: Option<String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableRenameIntentState {
    Prepared,
    SourceFenced,
    DestinationWritten,
    SourceTombstoned,
    IndexPublished,
    DestinationPublished,
    Completed,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableRenameIntent {
    pub version: u16,
    pub rename_id: String,
    pub table_bucket: String,
    pub source: TableEntry,
    pub destination: TableEntry,
    pub source_etag: String,
    pub destination_etag: Option<String>,
    pub warehouse_index_etag: String,
    pub state: TableRenameIntentState,
    pub created_at: String,
    pub updated_at: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct NamespaceEntry {
    pub version: u16,
    pub table_bucket: String,
    pub namespace: String,
    pub namespace_id: String,
    pub state: TableCatalogEntryState,
    #[serde(default)]
    pub properties: BTreeMap<String, String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

pub(crate) const NAMESPACE_PROPERTIES_MAX_ENTRIES: usize = 256;
pub(crate) const NAMESPACE_PROPERTY_KEY_MAX_LEN: usize = 256;
pub(crate) const NAMESPACE_PROPERTY_VALUE_MAX_LEN: usize = 4096;
pub(crate) const NAMESPACE_PROPERTIES_MAX_TOTAL_BYTES: usize = 64 * 1024;

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct NamespacePropertiesUpdate {
    removals: Vec<String>,
    updates: BTreeMap<String, String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum NamespacePropertiesUpdateError {
    DuplicateRemoval(String),
    Overlap(String),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct NamespacePropertiesUpdateResult {
    pub updated: Vec<String>,
    pub removed: Vec<String>,
    pub missing: Vec<String>,
}

impl NamespacePropertiesUpdate {
    pub(crate) fn try_new(
        removals: Vec<String>,
        updates: BTreeMap<String, String>,
    ) -> Result<Self, NamespacePropertiesUpdateError> {
        let mut removal_keys = BTreeSet::new();
        for key in &removals {
            if !removal_keys.insert(key.as_str()) {
                return Err(NamespacePropertiesUpdateError::DuplicateRemoval(key.clone()));
            }
            if updates.contains_key(key) {
                return Err(NamespacePropertiesUpdateError::Overlap(key.clone()));
            }
        }
        Ok(Self { removals, updates })
    }

    pub(crate) fn apply_to(self, entry: &mut NamespaceEntry) -> NamespacePropertiesUpdateResult {
        let updated = self.updates.keys().cloned().collect::<Vec<_>>();
        for (key, value) in self.updates {
            entry.properties.insert(key, value);
        }

        let mut removed = Vec::new();
        let mut missing = Vec::new();
        for key in self.removals {
            if entry.properties.remove(&key).is_some() {
                removed.push(key);
            } else {
                missing.push(key);
            }
        }

        NamespacePropertiesUpdateResult {
            updated,
            removed,
            missing,
        }
    }
}

pub(crate) fn validate_namespace_properties(properties: &BTreeMap<String, String>) -> TableCatalogStoreResult<()> {
    if properties.len() > NAMESPACE_PROPERTIES_MAX_ENTRIES {
        return Err(TableCatalogStoreError::Invalid(format!(
            "namespace properties exceed the maximum of {NAMESPACE_PROPERTIES_MAX_ENTRIES} entries"
        )));
    }

    let mut total_bytes = 0usize;
    for (key, value) in properties {
        if key.is_empty() || key.len() > NAMESPACE_PROPERTY_KEY_MAX_LEN {
            return Err(TableCatalogStoreError::Invalid(format!(
                "namespace property key length must be between 1 and {NAMESPACE_PROPERTY_KEY_MAX_LEN} bytes"
            )));
        }
        if value.len() > NAMESPACE_PROPERTY_VALUE_MAX_LEN {
            return Err(TableCatalogStoreError::Invalid(format!(
                "namespace property value exceeds {NAMESPACE_PROPERTY_VALUE_MAX_LEN} bytes"
            )));
        }
        total_bytes += key.len() + value.len();
        if total_bytes > NAMESPACE_PROPERTIES_MAX_TOTAL_BYTES {
            return Err(TableCatalogStoreError::Invalid(format!(
                "namespace properties exceed {NAMESPACE_PROPERTIES_MAX_TOTAL_BYTES} bytes"
            )));
        }
    }
    Ok(())
}

pub(crate) fn namespace_is_descendant(candidate: &str, parent: &str) -> bool {
    candidate.strip_prefix(parent).is_some_and(|suffix| suffix.starts_with('.'))
}

pub(crate) fn synthetic_namespace_entry(table_bucket: &str, namespace: &Namespace) -> NamespaceEntry {
    let namespace_id = namespace.storage_id();
    let namespace = namespace.public_name();
    NamespaceEntry {
        version: TABLE_CATALOG_ENTRY_VERSION,
        table_bucket: table_bucket.to_string(),
        namespace,
        namespace_id,
        state: TableCatalogEntryState::Active,
        properties: BTreeMap::new(),
        created_at: None,
        updated_at: None,
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableEntry {
    pub version: u16,
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub table_id: String,
    pub table_uuid: String,
    pub format: String,
    pub format_version: u16,
    pub warehouse_location: String,
    pub metadata_location: String,
    pub version_token: String,
    pub generation: u64,
    pub state: TableCatalogEntryState,
    #[serde(default)]
    pub properties: BTreeMap<String, String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableWarehouseIndexEntry {
    pub(super) version: u16,
    pub(super) table_bucket: String,
    pub(super) namespace: String,
    pub(super) table: String,
    pub(super) table_id: String,
    pub(super) warehouse_object_prefix: String,
    pub(super) state: TableCatalogEntryState,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableWarehouseIndexStateEntry {
    pub(super) version: u16,
    pub(super) table_bucket: String,
    pub(super) state: TableCatalogEntryState,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum WarehouseIndexReservation {
    Created,
    AlreadyReserved,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct ViewEntry {
    pub version: u16,
    pub table_bucket: String,
    pub namespace: String,
    pub view: String,
    pub view_id: String,
    pub view_uuid: String,
    pub format: String,
    pub format_version: u16,
    pub warehouse_location: String,
    pub metadata_location: String,
    pub version_token: String,
    pub generation: u64,
    pub state: TableCatalogEntryState,
    #[serde(default)]
    pub properties: BTreeMap<String, String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum CommitLogStatus {
    Staged,
    Committed,
    Failed,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct CommitLogEntry {
    pub version: u16,
    pub commit_id: String,
    pub idempotency_key: Option<String>,
    pub table_id: String,
    pub operation: String,
    pub expected_version_token: String,
    pub new_version_token: String,
    pub previous_metadata_location: String,
    pub new_metadata_location: String,
    #[serde(default)]
    pub requirements: Vec<serde_json::Value>,
    pub status: CommitLogStatus,
    pub writer: Option<String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableCommitRequest {
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub commit_id: String,
    pub idempotency_key: Option<String>,
    pub operation: String,
    pub expected_version_token: String,
    pub expected_metadata_location: String,
    pub new_metadata_location: String,
    #[serde(default)]
    pub requirements: Vec<serde_json::Value>,
    pub writer: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableCommitResult {
    pub table: TableEntry,
    pub commit_log: CommitLogEntry,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct ExternalCatalogBridgeEntry {
    pub version: u16,
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub catalog: String,
    pub external_catalog_id: Option<String>,
    pub external_namespace: String,
    pub external_table: String,
    pub external_table_uuid: Option<String>,
    pub metadata_location: Option<String>,
    pub external_version_token: Option<String>,
    pub policy_mode: String,
    pub credential_mode: String,
    pub sync_mode: String,
    pub rollback_strategy: String,
    pub last_sync_status: Option<String>,
    pub last_synced_metadata_location: Option<String>,
    #[serde(default)]
    pub properties: BTreeMap<String, String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct ViewCommitRequest {
    pub table_bucket: String,
    pub namespace: String,
    pub view: String,
    pub expected_version_token: String,
    pub expected_metadata_location: String,
    pub new_metadata_location: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct ViewCommitResult {
    pub view: ViewEntry,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableMaintenanceConfig {
    pub version: u16,
    #[serde(rename = "retain-recent-metadata-files")]
    pub retain_recent_metadata_files: usize,
    #[serde(rename = "delete-enabled")]
    pub delete_enabled: bool,
    #[serde(rename = "background-enabled")]
    pub background_enabled: bool,
    #[serde(default, rename = "worker-paused")]
    pub worker_paused: bool,
    #[serde(
        default = "default_table_maintenance_worker_lease_timeout_seconds",
        rename = "worker-lease-timeout-seconds"
    )]
    pub worker_lease_timeout_seconds: u64,
    #[serde(default, rename = "max-retry-attempts")]
    pub max_retry_attempts: u16,
    #[serde(default, rename = "retry-initial-backoff-seconds")]
    pub retry_initial_backoff_seconds: u64,
    #[serde(default, rename = "retry-max-backoff-seconds")]
    pub retry_max_backoff_seconds: u64,
    #[serde(default, rename = "quarantine-enabled")]
    pub quarantine_enabled: bool,
    #[serde(default, rename = "quarantine-retention-seconds")]
    pub quarantine_retention_seconds: u64,
}

impl Default for TableMaintenanceConfig {
    fn default() -> Self {
        Self {
            version: TABLE_MAINTENANCE_CONFIG_VERSION,
            retain_recent_metadata_files: 0,
            delete_enabled: false,
            background_enabled: false,
            worker_paused: false,
            worker_lease_timeout_seconds: TABLE_MAINTENANCE_WORKER_LEASE_TIMEOUT_DEFAULT_SECONDS,
            max_retry_attempts: 0,
            retry_initial_backoff_seconds: 5,
            retry_max_backoff_seconds: 300,
            quarantine_enabled: false,
            quarantine_retention_seconds: 0,
        }
    }
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceConfigSource {
    #[default]
    Default,
    TableBucketDefault,
    TableOverride,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableMaintenanceEffectiveConfig {
    pub config: TableMaintenanceConfig,
    pub source: TableMaintenanceConfigSource,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceSchedulerStatus {
    Ready,
    Queued,
    Disabled,
    Paused,
    Backpressured,
    RetryDeferred,
    Quarantined,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMaintenanceSchedulerReport {
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub table_id: String,
    pub status: TableMaintenanceSchedulerStatus,
    pub config_source: TableMaintenanceConfigSource,
    pub background_enabled: bool,
    pub worker_paused: bool,
    pub delete_enabled: bool,
    pub worker_lease_timeout_seconds: u64,
    pub max_retry_attempts: u16,
    pub retry_initial_backoff_seconds: u64,
    pub retry_max_backoff_seconds: u64,
    pub recommended_actions: Vec<TableMaintenanceRecommendedAction>,
    pub current_job: Option<TableMaintenanceSchedulerJobSummary>,
    pub quarantine: TableMaintenanceSchedulerQuarantineBoundary,
    pub audit_timeline: Vec<TableMaintenanceSchedulerJobSummary>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMaintenanceSchedulerRunResult {
    pub report: TableMetadataMaintenanceReport,
    pub scheduler: TableMaintenanceSchedulerReport,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMaintenanceSchedulerQuarantineBoundary {
    pub enabled: bool,
    pub active: bool,
    pub retention_seconds: u64,
    pub quarantined_object_count: usize,
    pub source_job_id: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceAuditActor {
    Scheduler,
    Worker,
    Operator,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceAuditAction {
    Planned,
    WorkerControl,
    SchedulerControl,
    SchedulerQueued,
    SchedulerLeaseExpired,
    WorkerStarted,
    WorkerHeartbeat,
    WorkerLeaseExpired,
    WorkerSucceeded,
    WorkerFailed,
    QuarantineRelease,
    QuarantineRetry,
    QuarantineAbandon,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMaintenanceAuditEvent {
    pub timestamp: String,
    pub actor: TableMaintenanceAuditActor,
    pub action: TableMaintenanceAuditAction,
    #[serde(default)]
    pub reason: Option<String>,
    #[serde(default, rename = "before-status")]
    pub before_status: Option<TableMetadataMaintenanceJobStatus>,
    #[serde(default, rename = "after-status")]
    pub after_status: Option<TableMetadataMaintenanceJobStatus>,
    #[serde(default, rename = "before-quarantined-object-count")]
    pub before_quarantined_object_count: Option<usize>,
    #[serde(default, rename = "after-quarantined-object-count")]
    pub after_quarantined_object_count: Option<usize>,
    #[serde(default, rename = "recommended-actions")]
    pub recommended_actions: Vec<TableMaintenanceRecommendedAction>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceQuarantineAction {
    Inspect,
    Release,
    Retry,
    Abandon,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableMaintenanceQuarantineOperationRequest {
    pub action: TableMaintenanceQuarantineAction,
    #[serde(default)]
    pub reason: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMaintenanceQuarantineOperationResult {
    pub action: TableMaintenanceQuarantineAction,
    pub report: TableMetadataMaintenanceReport,
    pub scheduler: TableMaintenanceSchedulerReport,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMaintenanceSchedulerJobSummary {
    pub job_id: String,
    pub operation: TableMetadataMaintenanceOperation,
    pub status: TableMetadataMaintenanceJobStatus,
    #[serde(default, rename = "scheduler-id")]
    pub scheduler_id: Option<String>,
    #[serde(default, rename = "scheduled-at")]
    pub scheduled_at: Option<String>,
    pub worker_id: Option<String>,
    pub attempt: u16,
    pub started_at: Option<String>,
    pub finished_at: Option<String>,
    pub heartbeat_at: Option<String>,
    pub next_retry_after: Option<String>,
    pub recommended_actions: Vec<TableMaintenanceRecommendedAction>,
    #[serde(default, rename = "audit-events")]
    pub audit_events: Vec<TableMaintenanceAuditEvent>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMetadataMaintenanceJob {
    pub job_id: String,
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub table_id: String,
    #[serde(default)]
    pub operation: TableMetadataMaintenanceOperation,
    #[serde(default)]
    pub status: TableMetadataMaintenanceJobStatus,
    #[serde(default)]
    pub failure_reason: Option<String>,
    #[serde(default, rename = "recommended-actions")]
    pub recommended_actions: Vec<TableMaintenanceRecommendedAction>,
    #[serde(default)]
    pub config_source: TableMaintenanceConfigSource,
    #[serde(default, rename = "scheduler-id")]
    pub scheduler_id: Option<String>,
    #[serde(default, rename = "scheduler-lease-id")]
    pub scheduler_lease_id: String,
    #[serde(default, rename = "scheduled-at")]
    pub scheduled_at: Option<String>,
    #[serde(default)]
    pub worker_id: Option<String>,
    #[serde(default)]
    pub lease_id: String,
    #[serde(default)]
    pub attempt: u16,
    #[serde(default, rename = "max-retry-attempts")]
    pub max_retry_attempts: u16,
    #[serde(default, rename = "next-retry-after")]
    pub next_retry_after: Option<String>,
    #[serde(default, rename = "quarantine-enabled")]
    pub quarantine_enabled: bool,
    #[serde(default, rename = "quarantine-retention-seconds")]
    pub quarantine_retention_seconds: u64,
    #[serde(default)]
    pub heartbeat_at: Option<String>,
    #[serde(default)]
    pub started_at: Option<String>,
    #[serde(default)]
    pub finished_at: Option<String>,
    pub current_metadata_location: String,
    pub current_generation: u64,
    pub retain_recent_metadata_files: usize,
    pub safety_window_seconds: i64,
    pub cleanup_watermark_unix_seconds: i64,
    #[serde(default)]
    pub planned_metadata_file_count: usize,
    #[serde(default)]
    pub retained_metadata_file_count: usize,
    #[serde(default)]
    pub cleanup_candidate_count: usize,
    #[serde(default)]
    pub deletable_metadata_file_count: usize,
    #[serde(default)]
    pub deleted_metadata_file_count: usize,
    #[serde(default)]
    pub planned_object_file_count: usize,
    #[serde(default)]
    pub cleanup_candidate_object_count: usize,
    #[serde(default)]
    pub deletable_object_count: usize,
    #[serde(default)]
    pub deleted_object_count: usize,
    #[serde(default)]
    pub quarantined_object_count: usize,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMetadataMaintenanceReport {
    pub job: TableMetadataMaintenanceJob,
    pub current_metadata_location: String,
    pub retained_metadata_locations: Vec<String>,
    pub cleanup_candidate_locations: Vec<String>,
    pub deletable_metadata_locations: Vec<String>,
    #[serde(default, rename = "cleanup-object-candidate-locations")]
    pub cleanup_object_candidate_locations: Vec<String>,
    #[serde(default, rename = "deletable-object-locations")]
    pub deletable_object_locations: Vec<String>,
    #[serde(default)]
    pub object_reports: Vec<TableMetadataMaintenanceObjectReport>,
    #[serde(default, rename = "object-cleanup-reports")]
    pub object_cleanup_reports: Vec<TableMetadataMaintenanceObjectCleanupReport>,
    #[serde(default)]
    pub referenced_object_reports: Vec<TableMetadataMaintenanceReferencedObjectReport>,
    #[serde(default, rename = "reachability-graph")]
    pub reachability_graph: TableMaintenanceReachabilityGraphReport,
    #[serde(default, rename = "snapshot-expiration")]
    pub snapshot_expiration: Option<TableSnapshotExpirationReport>,
    #[serde(default)]
    pub compaction: Option<TableCompactionPlanningReport>,
    #[serde(default, rename = "audit-events")]
    pub audit_events: Vec<TableMaintenanceAuditEvent>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMaintenanceReachabilityGraphReport {
    pub status: TableMaintenanceReachabilityGraphStatus,
    pub metadata_file_count: usize,
    pub manifest_list_count: usize,
    pub manifest_file_count: usize,
    pub data_file_count: usize,
    pub delete_file_count: usize,
    pub manual_review_count: usize,
    pub reasons: Vec<TableMaintenanceReachabilityGraphReason>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceReachabilityGraphStatus {
    Complete,
    #[default]
    ManualReviewRequired,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceReachabilityGraphReason {
    MetadataJsonParsed,
    ManifestListAvroReferenced,
    ManifestAvroReaderUnavailable,
    DataFileCleanupDeferred,
    DeleteFileCleanupDeferred,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableSnapshotExpirationConfig {
    #[serde(rename = "min-snapshots-to-keep")]
    pub min_snapshots_to_keep: usize,
    #[serde(rename = "max-snapshot-age-ms")]
    pub max_snapshot_age_ms: i64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableSnapshotExpirationReport {
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub table_id: String,
    pub current_metadata_location: String,
    pub current_snapshot_id: Option<i64>,
    pub config: TableSnapshotExpirationConfig,
    pub expiration_watermark_ms: i64,
    pub retained_snapshot_count: usize,
    pub expiration_candidate_count: usize,
    pub manual_review_count: usize,
    #[serde(default)]
    pub expired_snapshot_ids: Vec<i64>,
    #[serde(default)]
    pub committed_metadata_location: Option<String>,
    pub snapshot_reports: Vec<TableSnapshotExpirationSnapshotReport>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableSnapshotExpirationSnapshotReport {
    pub snapshot_id: Option<i64>,
    pub sequence_number: Option<i64>,
    pub timestamp_ms: Option<i64>,
    pub manifest_list: Option<String>,
    pub state: TableSnapshotExpirationSnapshotState,
    pub reasons: Vec<TableSnapshotExpirationReason>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableSnapshotExpirationSnapshotState {
    Retained,
    ExpirationCandidate,
    ManualReviewRequired,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableSnapshotExpirationReason {
    CurrentSnapshot,
    MinSnapshotsToKeep,
    ProtectedSnapshotRef,
    UserDefinedSnapshotRef,
    SnapshotRefRetentionConflict,
    TableRetentionPropertyConflict,
    MissingSnapshotId,
    MissingSnapshotTimestamp,
    SnapshotAgeWithinRetention,
    SnapshotAgeExpired,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct TableCompactionPlanningConfig {
    #[serde(rename = "target-file-size-bytes")]
    pub target_file_size_bytes: u64,
    #[serde(rename = "small-file-threshold-bytes")]
    pub small_file_threshold_bytes: u64,
    #[serde(rename = "min-input-files")]
    pub min_input_files: usize,
    #[serde(rename = "max-rewrite-bytes-per-job")]
    pub max_rewrite_bytes_per_job: u64,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableCompactionPlanningReport {
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub table_id: String,
    pub current_metadata_location: String,
    pub current_snapshot_id: Option<i64>,
    pub config: TableCompactionPlanningConfig,
    pub status: TableCompactionPlanningStatus,
    pub candidate_file_count: usize,
    pub rewrite_group_count: usize,
    pub manual_review_count: usize,
    #[serde(default, rename = "committed-metadata-location")]
    pub committed_metadata_location: Option<String>,
    #[serde(default, rename = "row-level-planning")]
    pub row_level_planning: TableRowLevelMaintenancePlanningReport,
    #[serde(default, rename = "rewrite-groups")]
    pub rewrite_groups: Vec<TableCompactionRewriteGroup>,
    pub snapshot_reports: Vec<TableCompactionSnapshotReport>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableRowLevelMaintenancePlanningReport {
    pub status: TableRowLevelMaintenancePlanningStatus,
    #[serde(rename = "delete-file-count")]
    pub delete_file_count: usize,
    #[serde(rename = "position-delete-file-count")]
    pub position_delete_file_count: usize,
    #[serde(rename = "equality-delete-file-count")]
    pub equality_delete_file_count: usize,
    #[serde(rename = "manual-review-count")]
    pub manual_review_count: usize,
    pub reasons: Vec<TableRowLevelMaintenancePlanningReason>,
    #[serde(rename = "delete-files")]
    pub delete_files: Vec<TableRowLevelDeleteFilePlanningReport>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableRowLevelMaintenancePlanningStatus {
    #[default]
    NoDeleteFiles,
    ManualReviewRequired,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableRowLevelMaintenancePlanningReason {
    PositionDeleteFile,
    EqualityDeleteFile,
    DeleteFileRewriteUnsupported,
    MissingDeleteFile,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableRowLevelDeleteFilePlanningReport {
    #[serde(rename = "file-location")]
    pub file_location: String,
    pub content: TableRowLevelDeleteFileContent,
    #[serde(rename = "object-exists")]
    pub object_exists: bool,
    #[serde(default, rename = "record-count", skip_serializing_if = "Option::is_none")]
    pub record_count: Option<u64>,
    #[serde(default, rename = "file-size-bytes", skip_serializing_if = "Option::is_none")]
    pub file_size_bytes: Option<u64>,
    #[serde(default, rename = "sequence-number", skip_serializing_if = "Option::is_none")]
    pub sequence_number: Option<i64>,
    #[serde(default, rename = "file-sequence-number", skip_serializing_if = "Option::is_none")]
    pub file_sequence_number: Option<i64>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableRowLevelDeleteFileContent {
    PositionDelete,
    EqualityDelete,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableCompactionRewriteGroup {
    pub group_id: String,
    #[serde(default, rename = "sort-order-id", skip_serializing_if = "Option::is_none")]
    pub sort_order_id: Option<i32>,
    #[serde(rename = "input-file-locations")]
    pub input_file_locations: Vec<String>,
    #[serde(rename = "input-file-count")]
    pub input_file_count: usize,
    #[serde(rename = "input-bytes")]
    pub input_bytes: u64,
    #[serde(default, rename = "output-file-location")]
    pub output_file_location: Option<String>,
    #[serde(default, rename = "output-bytes")]
    pub output_bytes: Option<u64>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableCompactionSnapshotReport {
    pub snapshot_id: Option<i64>,
    pub manifest_list: Option<String>,
    pub status: TableCompactionPlanningStatus,
    pub reasons: Vec<TableCompactionPlanningReason>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCompactionPlanningStatus {
    NoCandidates,
    RewriteCandidates,
    Committed,
    ManualReviewRequired,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCompactionPlanningReason {
    ManifestList,
    ManifestFile,
    SmallDataFile,
    RewriteGroup,
    CompactionCommitted,
    ManifestAvroReaderUnavailable,
    MissingCurrentSnapshot,
    MissingManifestList,
    MissingDataFile,
    DeleteFile,
    PositionDeleteFile,
    EqualityDeleteFile,
    RowLevelRewriteUnsupported,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMetadataMaintenanceOperation {
    #[default]
    DryRun,
    Delete,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMetadataMaintenanceJobStatus {
    NotYetRun,
    Queued,
    Running,
    #[default]
    Successful,
    Failed,
    Disabled,
    Paused,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMaintenanceRecommendedAction {
    NoActionRequired,
    RunMaintenanceWorker,
    ReviewAndRunDelete,
    ReviewQuarantine,
    EnableDelete,
    EnableBackgroundMaintenance,
    ResumeMaintenanceWorker,
    WaitForRetryBackoff,
    WaitForActiveWorker,
    InvestigateFailure,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMetadataMaintenanceObjectState {
    Retained,
    PendingSafetyWindow,
    Deletable,
    Deleted,
    ManualReviewRequired,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMetadataMaintenanceReason {
    CurrentMetadata,
    MetadataLog,
    ProtectedSnapshotRef,
    RecentMetadata,
    NoCurrentReachability,
    SafetyWindowPending,
    SafetyWindowSatisfied,
    DeletedByMaintenance,
    ManifestList,
    ManifestFile,
    DataFile,
    DeleteFile,
    UnsupportedManifestAvro,
    UnreadableMetadata,
    QuarantineEnabled,
    RetryScheduled,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMetadataMaintenanceObjectKind {
    MetadataFile,
    ManifestList,
    ManifestFile,
    DataFile,
    DeleteFile,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMetadataMaintenanceObjectReport {
    pub metadata_location: String,
    pub state: TableMetadataMaintenanceObjectState,
    pub reasons: Vec<TableMetadataMaintenanceReason>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMetadataMaintenanceObjectCleanupReport {
    pub object_location: String,
    pub object_kind: TableMetadataMaintenanceObjectKind,
    pub state: TableMetadataMaintenanceObjectState,
    pub reasons: Vec<TableMetadataMaintenanceReason>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TableMetadataMaintenanceReferencedObjectReport {
    pub object_location: String,
    pub object_kind: TableMetadataMaintenanceObjectKind,
    pub state: TableMetadataMaintenanceObjectState,
    pub reasons: Vec<TableMetadataMaintenanceReason>,
}

pub(crate) struct TableMaintenanceHeartbeatRef<'a> {
    pub(super) table_bucket: &'a str,
    pub(super) namespace: &'a str,
    pub(super) table: &'a str,
    pub(super) job_id: &'a str,
    pub(super) lease_id: &'a str,
    pub(super) worker_id: &'a str,
}

pub(crate) struct TableMaintenancePreflightContext<'a> {
    pub(super) table_bucket: &'a str,
    pub(super) namespace: &'a Namespace,
    pub(super) table: &'a IdentifierSegment,
    pub(super) entry: &'a TableEntry,
}

pub(crate) struct TableMaintenanceWorkerControlReport<'a> {
    pub(super) table_bucket: &'a str,
    pub(super) namespace: &'a Namespace,
    pub(super) table: &'a IdentifierSegment,
    pub(super) entry: &'a TableEntry,
    pub(super) worker_id: String,
    pub(super) effective: &'a TableMaintenanceEffectiveConfig,
    pub(super) status: TableMetadataMaintenanceJobStatus,
    pub(super) reason: &'a str,
    pub(super) now: OffsetDateTime,
}

pub(crate) struct TableMaintenanceSchedulerControlReport<'a> {
    pub(super) table_bucket: &'a str,
    pub(super) namespace: &'a Namespace,
    pub(super) table: &'a IdentifierSegment,
    pub(super) entry: &'a TableEntry,
    pub(super) scheduler_id: String,
    pub(super) effective: &'a TableMaintenanceEffectiveConfig,
    pub(super) status: TableMetadataMaintenanceJobStatus,
    pub(super) reason: &'a str,
    pub(super) now: OffsetDateTime,
}

pub(crate) enum TableMaintenanceWorkerPreflight {
    Ready {
        effective: TableMaintenanceEffectiveConfig,
        queued: Option<Box<TableMetadataMaintenanceReport>>,
    },
    Complete(Box<TableMetadataMaintenanceReport>),
}

pub(crate) enum TableMaintenanceSchedulerPreflight {
    Ready(TableMaintenanceEffectiveConfig),
    Complete(Box<TableMetadataMaintenanceReport>),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogExport {
    pub table_bucket: TableBucketEntry,
    pub namespace: NamespaceEntry,
    pub table: TableEntry,
    pub backing_manifest: TableCatalogBackingManifest,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogBackingManifest {
    pub version: u16,
    pub current: TableCatalogBackingProfile,
    pub migration: TableCatalogBackingMigrationPlan,
    pub ha: TableCatalogHaPolicy,
    pub scale_validation: TableCatalogScaleValidation,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogBackingProfile {
    pub kind: TableCatalogBackingKind,
    pub authority: TableCatalogAuthority,
    pub consistency: TableCatalogConsistencyMode,
    pub durability: TableCatalogDurabilityMode,
    pub current_pointer_path: String,
    pub wal: TableCatalogWalState,
    pub snapshot: TableCatalogSnapshotState,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogBackingKind {
    ObjectBacked,
    // RUSTFS_COMPAT_TODO(table-catalog-backing-manifest-v1-wire-labels): Keep the version 1 wire label for existing clients. Remove after a versioned manifest with an explicit client migration contract replaces it.
    #[serde(rename = "STRONG_KV_WAL")]
    DurableStrongSnapshot,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogAuthority {
    RustfsSysObject,
    #[allow(
        dead_code,
        reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
    )]
    LinearizableMetadataKv,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogConsistencyMode {
    ConditionalObjectCas,
    #[allow(
        dead_code,
        reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
    )]
    LinearizableCas,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogDurabilityMode {
    StagedCommitLogBeforePointerUpdate,
    #[allow(
        dead_code,
        reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
    )]
    WalBeforeStateMachineApply,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogWalState {
    pub status: TableCatalogWalStatus,
    pub commit_log_prefix: String,
    pub idempotency_index_prefix: String,
    pub committed_generation: u64,
    pub staged_before_table_update_count: usize,
    pub finalization_required_count: usize,
    pub idempotency_repair_required_count: usize,
    pub manual_review_count: usize,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogWalStatus {
    Recoverable,
    RecoveryRequired,
    ManualReviewRequired,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogSnapshotState {
    pub export_api: String,
    pub includes_table_bucket: bool,
    pub includes_namespace: bool,
    pub includes_table_pointer: bool,
    pub includes_backing_manifest: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogBackingMigrationPlan {
    pub source_kind: TableCatalogBackingKind,
    pub target_kind: TableCatalogBackingKind,
    pub status: TableCatalogBackingMigrationStatus,
    pub required_steps: Vec<TableCatalogBackingMigrationStep>,
    pub blockers: Vec<TableCatalogBackingMigrationBlocker>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogBackingMigrationDryRunReport {
    pub table_bucket: String,
    pub source_kind: TableCatalogBackingKind,
    pub target_kind: TableCatalogBackingKind,
    pub status: TableCatalogBackingMigrationStatus,
    pub namespace_count: usize,
    pub table_count: usize,
    pub view_count: usize,
    pub commit_log_count: usize,
    pub idempotency_index_count: usize,
    pub warehouse_prefix_count: usize,
    pub warehouse_index_ready: bool,
    pub object_backed_writes_fenced: bool,
    pub ready_to_enable_durable_strong: bool,
    pub blockers: Vec<TableCatalogBackingMigrationBlocker>,
    pub recommended_actions: Vec<TableCatalogBackingMigrationAction>,
    pub rollback: TableCatalogBackingRollbackPlan,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogBackingMigrationExecutionReport {
    pub table_bucket: String,
    pub source_kind: TableCatalogBackingKind,
    pub target_kind: TableCatalogBackingKind,
    pub status: TableCatalogBackingMigrationExecutionStatus,
    pub namespace_count: usize,
    pub table_count: usize,
    pub view_count: usize,
    pub commit_log_count: usize,
    pub idempotency_index_count: usize,
    pub source_fingerprint: String,
    pub target_snapshot_etag: String,
    pub object_backed_writes_fenced: bool,
    pub ready_to_enable_durable_strong: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogBackingMigrationExecutionStatus {
    SnapshotMaterialized,
    SnapshotAlreadyMaterialized,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogBackingMigrationCancelReport {
    pub table_bucket: String,
    pub status: TableCatalogBackingMigrationCancelStatus,
    pub object_backed_writes_fenced: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogBackingMigrationCancelStatus {
    FenceReleased,
    NoMigrationFence,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogBackingMigrationStatus {
    ReadyToSnapshot,
    SnapshotMaterialized,
    RecoveryRequired,
    ManualReviewRequired,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogBackingMigrationStep {
    SnapshotCatalogExport,
    ReplayCommitLog,
    VerifyCurrentPointer,
    EnableSingleWriterFencing,
    // RUSTFS_COMPAT_TODO(table-catalog-backing-manifest-v1-wire-labels): Keep the version 1 wire label for existing clients. Remove after a versioned manifest with an explicit client migration contract replaces it.
    #[serde(rename = "CUT_OVER_LINEARIZABLE_READS")]
    CutOverDurableSnapshotReads,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogBackingMigrationBlocker {
    TableRenameRecoveryRequired,
    CommitRecoveryRequired,
    CommitManualReviewRequired,
    WarehouseIndexBackfillRequired,
    DuplicateWarehousePrefix,
    DuplicateTableIdentity,
    TableViewIdentifierCollision,
    DurableStrongSnapshotChanged,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogBackingMigrationAction {
    RunCatalogRecovery,
    BackfillWarehouseIndex,
    ReviewDuplicateWarehousePrefixes,
    ReviewDuplicateTableIdentities,
    ReviewTableViewIdentifierCollisions,
    SnapshotObjectBackedCatalog,
    EnableDurableStrongBacking,
    VerifyDurableStrongSnapshot,
    SnapshotRemainingTableBuckets,
    ReviewDurableStrongSnapshot,
    KeepObjectBackedRollbackConfig,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogBackingRollbackPlan {
    pub backing_config_key: &'static str,
    pub current_backing_value: &'static str,
    pub rollback_backing_value: &'static str,
    pub preserves_object_backed_catalog: bool,
    pub requires_operator_restart: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogHaPolicy {
    pub writer_region_model: TableCatalogHaWriterModel,
    pub read_replica_strategy: TableCatalogReadReplicaStrategy,
    pub commit_read_requirement: TableCatalogCommitReadRequirement,
    pub active_active_supported: bool,
    pub failover_requires_operator_promotion: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogHaWriterModel {
    SingleActiveWriterRegion,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogReadReplicaStrategy {
    ReadOnlyReplicasForListAndLoad,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogCommitReadRequirement {
    LinearizableLeaderRead,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogScaleValidation {
    pub status: TableCatalogScaleValidationStatus,
    pub benchmark_required: bool,
    pub required_scenarios: Vec<TableCatalogScaleValidationScenario>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogScaleValidationStatus {
    MatrixPublished,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogScaleValidationScenario {
    ConcurrentCommitCas,
    CommitLogRecoveryReplay,
    MigrationSnapshotReplay,
    ReadReplicaStaleReadGuard,
    ClientConformanceMatrix,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct TableDataPlaneResource {
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub table_id: String,
    pub warehouse_object_prefix: String,
}

impl TableDataPlaneResource {
    pub(crate) fn catalog_resource_object(&self) -> String {
        let namespace = Namespace::parse(&self.namespace)
            .map(|namespace| namespace.storage_id())
            .unwrap_or_else(|_| self.namespace.clone());
        format!("{NAMESPACE_ROOT}/{namespace}/{TABLE_ROOT}/{}", self.table)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableMetadataPointerStatus {
    Valid,
    MissingObject,
    InvalidLocation,
    InvalidJson,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogRecoveryStatus {
    Healthy,
    Recoverable,
    ManualReviewRequired,
    ReadOnlyRecommended,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCatalogRecoveryAction {
    RunCommitRecovery,
    RetryCommit,
    RestoreCurrentMetadataObject,
    FixCurrentMetadataJson,
    MoveCurrentMetadataInsideTable,
    ReviewCommitLog,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCatalogDiagnosticsReport {
    pub catalog: TableCatalogExport,
    pub current_metadata_status: TableMetadataPointerStatus,
    pub recovery_status: TableCatalogRecoveryStatus,
    pub recommended_actions: Vec<TableCatalogRecoveryAction>,
    pub commit_recovery: TableCommitRecoveryReport,
    pub backing_manifest: TableCatalogBackingManifest,
    pub orphan_metadata_candidate_locations: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCommitRecoveryState {
    Committed,
    StagedBeforeTableUpdate,
    FinalizationRequired,
    IdempotencyIndexRepairRequired,
    ManualReview,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub(crate) enum TableCommitIdempotencyIndexStatus {
    NotRequired,
    Missing,
    Matches,
    Stale,
    Conflicting,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCommitRecoveryEntry {
    pub commit_id: String,
    pub idempotency_key: Option<String>,
    pub operation: String,
    pub status: CommitLogStatus,
    pub recovery_state: TableCommitRecoveryState,
    pub previous_metadata_location: String,
    pub new_metadata_location: String,
    pub expected_version_token: String,
    pub new_version_token: String,
    pub idempotency_index_present: bool,
    pub idempotency_index_status: TableCommitIdempotencyIndexStatus,
    pub reason: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct TableCommitRecoveryReport {
    pub table_bucket: String,
    pub namespace: String,
    pub table: String,
    pub table_id: String,
    pub current_metadata_location: String,
    pub current_version_token: String,
    pub current_generation: u64,
    pub commits: Vec<TableCommitRecoveryEntry>,
    pub staged_before_table_update_count: usize,
    pub finalization_required_count: usize,
    pub idempotency_repair_required_count: usize,
    pub manual_review_count: usize,
    pub finalized_count: usize,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[allow(
    dead_code,
    reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
)]
pub(crate) struct NamespaceMarker {
    pub version: u16,
    pub namespace: String,
}

impl NamespaceMarker {
    #[allow(
        dead_code,
        reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
    )]
    pub fn new(namespace: &Namespace) -> Self {
        Self {
            version: TABLE_NAMESPACE_MARKER_VERSION,
            namespace: namespace.public_name(),
        }
    }
}

#[allow(
    dead_code,
    reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
)]
pub(crate) fn namespace_marker_json(namespace: &Namespace) -> Result<Vec<u8>, serde_json::Error> {
    serde_json::to_vec(&NamespaceMarker::new(namespace))
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[allow(
    dead_code,
    reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
)]
pub(crate) struct TableMarker {
    pub version: u16,
    pub namespace: String,
    pub name: String,
    pub metadata_location: Option<String>,
}

impl TableMarker {
    #[allow(
        dead_code,
        reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
    )]
    pub fn new(namespace: &Namespace, table: &IdentifierSegment) -> Self {
        Self {
            version: TABLE_RESOURCE_MARKER_VERSION,
            namespace: namespace.public_name(),
            name: table.as_str().to_string(),
            metadata_location: None,
        }
    }
}

#[allow(
    dead_code,
    reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
)]
pub(crate) fn table_marker_json(namespace: &Namespace, table: &IdentifierSegment) -> Result<Vec<u8>, serde_json::Error> {
    serde_json::to_vec(&TableMarker::new(namespace, table))
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[allow(
    dead_code,
    reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
)]
pub(crate) struct TableMetadataPointer {
    pub version: u16,
    pub metadata_location: String,
}

impl TableMetadataPointer {
    #[allow(
        dead_code,
        reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
    )]
    pub fn new(metadata_location: String) -> Self {
        Self {
            version: TABLE_METADATA_POINTER_VERSION,
            metadata_location,
        }
    }
}

#[allow(
    dead_code,
    reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
)]
pub(crate) fn table_metadata_pointer_json(metadata_location: String) -> Result<Vec<u8>, serde_json::Error> {
    serde_json::to_vec(&TableMetadataPointer::new(metadata_location))
}

#[allow(
    dead_code,
    reason = "exercised by table_catalog/tests.rs; the lib target cannot see test-only consumers (backlog#1823)"
)]
pub(crate) fn parse_table_metadata_pointer(data: &[u8]) -> Result<TableMetadataPointer, serde_json::Error> {
    serde_json::from_slice(data)
}
