use std::collections::{BTreeMap, HashSet};
use std::fmt;
use std::sync::Arc;

use chrono::{DateTime, Utc};
use fakecloud_aws::arn::Arn;
use parking_lot::RwLock;
use uuid::Uuid;

pub type SharedRdsState = Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<RdsState>>>;

impl fakecloud_core::multi_account::AccountState for RdsState {
    fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
        Self::new(account_id, region)
    }
}

/// Supported DB instance classes — single source of truth.
pub const SUPPORTED_INSTANCE_CLASSES: &[&str] = &[
    "db.t3.micro",
    "db.t3.small",
    "db.t3.medium",
    "db.t3.large",
    "db.t4g.micro",
    "db.t4g.small",
    "db.m5.large",
];

#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct DbInstance {
    pub db_instance_identifier: String,
    pub db_instance_arn: String,
    pub db_instance_class: String,
    pub engine: String,
    pub engine_version: String,
    pub db_instance_status: String,
    pub master_username: String,
    pub db_name: Option<String>,
    pub endpoint_address: String,
    pub port: i32,
    pub allocated_storage: i32,
    pub publicly_accessible: bool,
    pub deletion_protection: bool,
    pub created_at: DateTime<Utc>,
    pub dbi_resource_id: String,
    pub master_user_password: String,
    pub container_id: String,
    pub host_port: u16,
    pub tags: Vec<RdsTag>,
    pub read_replica_source_db_instance_identifier: Option<String>,
    pub read_replica_db_instance_identifiers: Vec<String>,
    pub vpc_security_group_ids: Vec<String>,
    pub db_parameter_group_name: Option<String>,
    pub backup_retention_period: i32,
    pub preferred_backup_window: String,
    pub latest_restorable_time: Option<DateTime<Utc>>,
    pub option_group_name: Option<String>,
    pub multi_az: bool,
    pub pending_modified_values: Option<PendingModifiedValues>,
}

#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct PendingModifiedValues {
    pub db_instance_class: Option<String>,
    pub allocated_storage: Option<i32>,
    pub backup_retention_period: Option<i32>,
    pub multi_az: Option<bool>,
    pub engine_version: Option<String>,
    pub master_user_password: Option<String>,
}

impl fmt::Debug for PendingModifiedValues {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("PendingModifiedValues")
            .field("db_instance_class", &self.db_instance_class)
            .field("allocated_storage", &self.allocated_storage)
            .field("backup_retention_period", &self.backup_retention_period)
            .field("multi_az", &self.multi_az)
            .field("engine_version", &self.engine_version)
            .field(
                "master_user_password",
                &self.master_user_password.as_ref().map(|_| "<redacted>"),
            )
            .finish()
    }
}

impl fmt::Debug for DbInstance {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DbInstance")
            .field("db_instance_identifier", &self.db_instance_identifier)
            .field("db_instance_arn", &self.db_instance_arn)
            .field("db_instance_class", &self.db_instance_class)
            .field("engine", &self.engine)
            .field("engine_version", &self.engine_version)
            .field("db_instance_status", &self.db_instance_status)
            .field("master_username", &self.master_username)
            .field("db_name", &self.db_name)
            .field("endpoint_address", &self.endpoint_address)
            .field("port", &self.port)
            .field("allocated_storage", &self.allocated_storage)
            .field("publicly_accessible", &self.publicly_accessible)
            .field("deletion_protection", &self.deletion_protection)
            .field("created_at", &self.created_at)
            .field("dbi_resource_id", &self.dbi_resource_id)
            .field("master_user_password", &"<redacted>")
            .field("container_id", &self.container_id)
            .field("host_port", &self.host_port)
            .field("tags", &self.tags)
            .field(
                "read_replica_source_db_instance_identifier",
                &self.read_replica_source_db_instance_identifier,
            )
            .field(
                "read_replica_db_instance_identifiers",
                &self.read_replica_db_instance_identifiers,
            )
            .field("vpc_security_group_ids", &self.vpc_security_group_ids)
            .field("db_parameter_group_name", &self.db_parameter_group_name)
            .field("backup_retention_period", &self.backup_retention_period)
            .field("preferred_backup_window", &self.preferred_backup_window)
            .field("latest_restorable_time", &self.latest_restorable_time)
            .field("option_group_name", &self.option_group_name)
            .field("multi_az", &self.multi_az)
            .field("pending_modified_values", &self.pending_modified_values)
            .finish()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct RdsTag {
    pub key: String,
    pub value: String,
}

#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct DbSnapshot {
    pub db_snapshot_identifier: String,
    pub db_snapshot_arn: String,
    pub db_instance_identifier: String,
    pub snapshot_create_time: DateTime<Utc>,
    pub engine: String,
    pub engine_version: String,
    pub allocated_storage: i32,
    pub status: String,
    pub port: i32,
    pub master_username: String,
    pub db_name: Option<String>,
    pub dbi_resource_id: String,
    pub snapshot_type: String,
    pub master_user_password: String,
    pub tags: Vec<RdsTag>,
    pub dump_data: Vec<u8>,
}

impl fmt::Debug for DbSnapshot {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DbSnapshot")
            .field("db_snapshot_identifier", &self.db_snapshot_identifier)
            .field("db_snapshot_arn", &self.db_snapshot_arn)
            .field("db_instance_identifier", &self.db_instance_identifier)
            .field("snapshot_create_time", &self.snapshot_create_time)
            .field("engine", &self.engine)
            .field("engine_version", &self.engine_version)
            .field("allocated_storage", &self.allocated_storage)
            .field("status", &self.status)
            .field("port", &self.port)
            .field("master_username", &self.master_username)
            .field("db_name", &self.db_name)
            .field("dbi_resource_id", &self.dbi_resource_id)
            .field("snapshot_type", &self.snapshot_type)
            .field("master_user_password", &"<redacted>")
            .field("tags", &self.tags)
            .field("dump_data", &format!("<{} bytes>", self.dump_data.len()))
            .finish()
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RdsState {
    pub account_id: String,
    pub region: String,
    pub instances: BTreeMap<String, DbInstance>,
    pub in_progress_instance_ids: HashSet<String>,
    pub snapshots: BTreeMap<String, DbSnapshot>,
    pub subnet_groups: BTreeMap<String, DbSubnetGroup>,
    pub parameter_groups: BTreeMap<String, DbParameterGroup>,
    /// Generic stores keyed by category (clusters, cluster_snapshots,
    /// cluster_param_groups, proxies, proxy_endpoints, security_groups,
    /// option_groups, event_subscriptions, global_clusters, integrations,
    /// blue_green, shard_groups, custom_engine_versions, tenant_dbs,
    /// export_tasks, etc.) so the extras handlers can persist state
    /// without proliferating per-category fields.
    #[serde(default)]
    pub extras: BTreeMap<String, BTreeMap<String, serde_json::Value>>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EngineVersionInfo {
    pub engine: String,
    pub engine_version: String,
    pub db_parameter_group_family: String,
    pub db_engine_description: String,
    pub db_engine_version_description: String,
    pub status: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OrderableDbInstanceOption {
    pub engine: String,
    pub engine_version: String,
    pub db_instance_class: String,
    pub license_model: String,
    pub storage_type: String,
    pub min_storage_size: i32,
    pub max_storage_size: i32,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DbSubnetGroup {
    pub db_subnet_group_name: String,
    pub db_subnet_group_arn: String,
    pub db_subnet_group_description: String,
    pub vpc_id: String,
    pub subnet_ids: Vec<String>,
    pub subnet_availability_zones: Vec<String>,
    pub tags: Vec<RdsTag>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DbParameterGroup {
    pub db_parameter_group_name: String,
    pub db_parameter_group_arn: String,
    pub db_parameter_group_family: String,
    pub description: String,
    pub parameters: BTreeMap<String, String>,
    pub tags: Vec<RdsTag>,
}

impl RdsState {
    pub fn new(account_id: &str, region: &str) -> Self {
        Self {
            account_id: account_id.to_string(),
            region: region.to_string(),
            instances: BTreeMap::new(),
            in_progress_instance_ids: HashSet::new(),
            snapshots: BTreeMap::new(),
            subnet_groups: BTreeMap::new(),
            parameter_groups: default_parameter_groups(account_id, region),
            extras: BTreeMap::new(),
        }
    }

    pub fn reset(&mut self) {
        self.instances.clear();
        self.in_progress_instance_ids.clear();
        self.snapshots.clear();
        self.subnet_groups.clear();
        self.parameter_groups = default_parameter_groups(&self.account_id, &self.region);
        self.extras.clear();
    }

    pub fn db_instance_arn(&self, db_instance_identifier: &str) -> String {
        Arn::new(
            "rds",
            &self.region,
            &self.account_id,
            &format!("db:{db_instance_identifier}"),
        )
        .to_string()
    }

    pub fn db_snapshot_arn(&self, db_snapshot_identifier: &str) -> String {
        Arn::new(
            "rds",
            &self.region,
            &self.account_id,
            &format!("snapshot:{db_snapshot_identifier}"),
        )
        .to_string()
    }

    pub fn db_subnet_group_arn(&self, db_subnet_group_name: &str) -> String {
        Arn::new(
            "rds",
            &self.region,
            &self.account_id,
            &format!("subgrp:{db_subnet_group_name}"),
        )
        .to_string()
    }

    pub fn db_parameter_group_arn(&self, db_parameter_group_name: &str) -> String {
        Arn::new(
            "rds",
            &self.region,
            &self.account_id,
            &format!("pg:{db_parameter_group_name}"),
        )
        .to_string()
    }

    pub fn next_dbi_resource_id(&self) -> String {
        format!("db-{}", Uuid::new_v4().simple())
    }

    pub fn begin_instance_creation(&mut self, db_instance_identifier: &str) -> bool {
        if self.instances.contains_key(db_instance_identifier)
            || self
                .in_progress_instance_ids
                .contains(db_instance_identifier)
        {
            return false;
        }

        self.in_progress_instance_ids
            .insert(db_instance_identifier.to_string());
        true
    }

    pub fn finish_instance_creation(&mut self, instance: DbInstance) {
        self.in_progress_instance_ids
            .remove(&instance.db_instance_identifier);
        self.instances
            .insert(instance.db_instance_identifier.clone(), instance);
    }

    pub fn cancel_instance_creation(&mut self, db_instance_identifier: &str) {
        self.in_progress_instance_ids.remove(db_instance_identifier);
    }
}

pub fn default_engine_versions() -> Vec<EngineVersionInfo> {
    vec![
        // PostgreSQL versions
        EngineVersionInfo {
            engine: "postgres".to_string(),
            engine_version: "16.3".to_string(),
            db_parameter_group_family: "postgres16".to_string(),
            db_engine_description: "PostgreSQL".to_string(),
            db_engine_version_description: "PostgreSQL 16.3".to_string(),
            status: "available".to_string(),
        },
        EngineVersionInfo {
            engine: "postgres".to_string(),
            engine_version: "15.5".to_string(),
            db_parameter_group_family: "postgres15".to_string(),
            db_engine_description: "PostgreSQL".to_string(),
            db_engine_version_description: "PostgreSQL 15.5".to_string(),
            status: "available".to_string(),
        },
        EngineVersionInfo {
            engine: "postgres".to_string(),
            engine_version: "14.10".to_string(),
            db_parameter_group_family: "postgres14".to_string(),
            db_engine_description: "PostgreSQL".to_string(),
            db_engine_version_description: "PostgreSQL 14.10".to_string(),
            status: "available".to_string(),
        },
        EngineVersionInfo {
            engine: "postgres".to_string(),
            engine_version: "13.13".to_string(),
            db_parameter_group_family: "postgres13".to_string(),
            db_engine_description: "PostgreSQL".to_string(),
            db_engine_version_description: "PostgreSQL 13.13".to_string(),
            status: "available".to_string(),
        },
        // MySQL versions
        EngineVersionInfo {
            engine: "mysql".to_string(),
            engine_version: "8.0.35".to_string(),
            db_parameter_group_family: "mysql8.0".to_string(),
            db_engine_description: "MySQL Community Edition".to_string(),
            db_engine_version_description: "MySQL 8.0.35".to_string(),
            status: "available".to_string(),
        },
        EngineVersionInfo {
            engine: "mysql".to_string(),
            engine_version: "8.0.28".to_string(),
            db_parameter_group_family: "mysql8.0".to_string(),
            db_engine_description: "MySQL Community Edition".to_string(),
            db_engine_version_description: "MySQL 8.0.28".to_string(),
            status: "available".to_string(),
        },
        EngineVersionInfo {
            engine: "mysql".to_string(),
            engine_version: "5.7.44".to_string(),
            db_parameter_group_family: "mysql5.7".to_string(),
            db_engine_description: "MySQL Community Edition".to_string(),
            db_engine_version_description: "MySQL 5.7.44".to_string(),
            status: "available".to_string(),
        },
        // MariaDB versions
        EngineVersionInfo {
            engine: "mariadb".to_string(),
            engine_version: "11.4.5".to_string(),
            db_parameter_group_family: "mariadb11.4".to_string(),
            db_engine_description: "MariaDB Community Edition".to_string(),
            db_engine_version_description: "MariaDB 11.4.5".to_string(),
            status: "available".to_string(),
        },
        EngineVersionInfo {
            engine: "mariadb".to_string(),
            engine_version: "10.11.6".to_string(),
            db_parameter_group_family: "mariadb10.11".to_string(),
            db_engine_description: "MariaDB Community Edition".to_string(),
            db_engine_version_description: "MariaDB 10.11.6".to_string(),
            status: "available".to_string(),
        },
        EngineVersionInfo {
            engine: "mariadb".to_string(),
            engine_version: "10.6.16".to_string(),
            db_parameter_group_family: "mariadb10.6".to_string(),
            db_engine_description: "MariaDB Community Edition".to_string(),
            db_engine_version_description: "MariaDB 10.6.16".to_string(),
            status: "available".to_string(),
        },
    ]
}

pub fn default_orderable_options() -> Vec<OrderableDbInstanceOption> {
    let mut options = Vec::new();
    let engines_and_versions = vec![
        ("postgres", "16.3", "postgresql-license"),
        ("postgres", "15.5", "postgresql-license"),
        ("postgres", "14.10", "postgresql-license"),
        ("postgres", "13.13", "postgresql-license"),
        ("mysql", "8.0.35", "general-public-license"),
        ("mysql", "8.0.28", "general-public-license"),
        ("mysql", "5.7.44", "general-public-license"),
        ("mariadb", "11.4.5", "general-public-license"),
        ("mariadb", "10.11.6", "general-public-license"),
        ("mariadb", "10.6.16", "general-public-license"),
    ];

    for (engine, version, license) in engines_and_versions {
        for class in SUPPORTED_INSTANCE_CLASSES {
            options.push(OrderableDbInstanceOption {
                engine: engine.to_string(),
                engine_version: version.to_string(),
                db_instance_class: class.to_string(),
                license_model: license.to_string(),
                storage_type: "gp2".to_string(),
                min_storage_size: 20,
                max_storage_size: 16384,
            });
        }
    }

    options
}

pub fn default_parameter_groups(
    account_id: &str,
    region: &str,
) -> BTreeMap<String, DbParameterGroup> {
    let mut groups = BTreeMap::new();

    let families = vec![
        ("postgres16", "Default parameter group for postgres16"),
        ("postgres15", "Default parameter group for postgres15"),
        ("postgres14", "Default parameter group for postgres14"),
        ("postgres13", "Default parameter group for postgres13"),
        ("mysql8.0", "Default parameter group for mysql8.0"),
        ("mysql5.7", "Default parameter group for mysql5.7"),
        ("mariadb11.4", "Default parameter group for mariadb11.4"),
        ("mariadb10.11", "Default parameter group for mariadb10.11"),
        ("mariadb10.6", "Default parameter group for mariadb10.6"),
        // Heavy-engine families. The names match what
        // `service::default_parameter_group` returns so callers that
        // omit `DBParameterGroupName` get a hit instead of a
        // `DBParameterGroupNotFound`.
        ("oracle-ee-23", "Default parameter group for oracle-ee-23"),
        ("oracle-ee-21", "Default parameter group for oracle-ee-21"),
        ("oracle-ee-19", "Default parameter group for oracle-ee-19"),
        ("oracle-se2-23", "Default parameter group for oracle-se2-23"),
        ("oracle-se2-21", "Default parameter group for oracle-se2-21"),
        ("oracle-se2-19", "Default parameter group for oracle-se2-19"),
        (
            "oracle-ee-cdb-23",
            "Default parameter group for oracle-ee-cdb-23",
        ),
        (
            "oracle-se2-cdb-23",
            "Default parameter group for oracle-se2-cdb-23",
        ),
        (
            "sqlserver-ee-16",
            "Default parameter group for sqlserver-ee-16",
        ),
        (
            "sqlserver-ee-15",
            "Default parameter group for sqlserver-ee-15",
        ),
        (
            "sqlserver-se-16",
            "Default parameter group for sqlserver-se-16",
        ),
        (
            "sqlserver-se-15",
            "Default parameter group for sqlserver-se-15",
        ),
        (
            "sqlserver-ex-16",
            "Default parameter group for sqlserver-ex-16",
        ),
        (
            "sqlserver-ex-15",
            "Default parameter group for sqlserver-ex-15",
        ),
        (
            "sqlserver-web-16",
            "Default parameter group for sqlserver-web-16",
        ),
        (
            "sqlserver-web-15",
            "Default parameter group for sqlserver-web-15",
        ),
        ("db2-se-11.5", "Default parameter group for db2-se-11.5"),
        ("db2-ae-11.5", "Default parameter group for db2-ae-11.5"),
    ];

    for (family, description) in families {
        let group_name = format!("default.{}", family);
        let group = DbParameterGroup {
            db_parameter_group_name: group_name.clone(),
            db_parameter_group_arn: Arn::new(
                "rds",
                region,
                account_id,
                &format!("pg:{group_name}"),
            )
            .to_string(),
            db_parameter_group_family: family.to_string(),
            description: description.to_string(),
            parameters: BTreeMap::new(),
            tags: Vec::new(),
        };
        groups.insert(group_name, group);
    }

    groups
}

pub const RDS_SNAPSHOT_SCHEMA_VERSION: u32 = 2;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RdsSnapshot {
    pub schema_version: u32,
    #[serde(default)]
    pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<RdsState>>,
    #[serde(default)]
    pub state: Option<RdsState>,
}

#[cfg(test)]
mod tests {
    use chrono::Utc;

    use super::{
        default_engine_versions, default_orderable_options, default_parameter_groups, DbInstance,
        RdsState,
    };

    #[test]
    fn new_initializes_account_and_region() {
        let state = RdsState::new("123456789012", "us-east-1");

        assert_eq!(state.account_id, "123456789012");
        assert_eq!(state.region, "us-east-1");
        assert!(state.instances.is_empty());
        assert!(state.in_progress_instance_ids.is_empty());
    }

    #[test]
    fn reset_clears_instances() {
        let mut state = RdsState::new("123456789012", "us-east-1");
        let created_at = Utc::now();
        state.instances.insert(
            "db-1".to_string(),
            DbInstance {
                db_instance_identifier: "db-1".to_string(),
                db_instance_arn: "arn:aws:rds:us-east-1:123456789012:db:db-1".to_string(),
                db_instance_class: "db.t3.micro".to_string(),
                engine: "postgres".to_string(),
                engine_version: "16.3".to_string(),
                db_instance_status: "available".to_string(),
                master_username: "admin".to_string(),
                db_name: Some("postgres".to_string()),
                endpoint_address: "127.0.0.1".to_string(),
                port: 5432,
                allocated_storage: 20,
                publicly_accessible: true,
                deletion_protection: false,
                created_at,
                dbi_resource_id: "db-test".to_string(),
                master_user_password: "secret123".to_string(),
                container_id: "container-id".to_string(),
                host_port: 15432,
                tags: Vec::new(),
                read_replica_source_db_instance_identifier: None,
                read_replica_db_instance_identifiers: Vec::new(),
                vpc_security_group_ids: Vec::new(),
                db_parameter_group_name: None,
                backup_retention_period: 1,
                preferred_backup_window: "03:00-04:00".to_string(),
                latest_restorable_time: Some(created_at),
                option_group_name: None,
                multi_az: false,
                pending_modified_values: None,
            },
        );

        state.reset();

        assert!(state.instances.is_empty());
        assert!(state.in_progress_instance_ids.is_empty());
    }

    #[test]
    fn default_engine_versions_are_postgres_metadata() {
        let versions = default_engine_versions();

        assert_eq!(versions.len(), 10); // 4 postgres + 3 mysql + 3 mariadb
                                        // Check first postgres version
        assert_eq!(versions[0].engine, "postgres");
        assert_eq!(versions[0].engine_version, "16.3");
        assert_eq!(versions[0].db_parameter_group_family, "postgres16");
    }

    #[test]
    fn default_orderable_options_match_engine_versions() {
        let versions = default_engine_versions();
        let options = default_orderable_options();

        assert_eq!(options.len(), 70); // 10 versions * 7 instance classes
                                       // Verify all engines and versions have orderable options
        for version in &versions {
            assert!(options.iter().any(|opt| {
                opt.engine == version.engine && opt.engine_version == version.engine_version
            }));
        }
    }

    #[test]
    fn begin_instance_creation_rejects_duplicate_identifiers() {
        let mut state = RdsState::new("123456789012", "us-east-1");

        assert!(state.begin_instance_creation("db-1"));
        assert!(!state.begin_instance_creation("db-1"));

        state.cancel_instance_creation("db-1");
        assert!(state.begin_instance_creation("db-1"));
    }

    #[test]
    fn arn_helpers_format_correctly() {
        let state = RdsState::new("123456789012", "eu-west-1");
        assert!(state.db_instance_arn("mydb").contains(":db:mydb"));
        assert!(state.db_snapshot_arn("snap1").contains(":snapshot:snap1"));
        assert!(state.db_subnet_group_arn("sng").contains("sng"));
        assert!(state.db_parameter_group_arn("pg").contains("pg"));
    }

    #[test]
    fn next_dbi_resource_id_format() {
        let state = RdsState::new("123456789012", "us-east-1");
        let id = state.next_dbi_resource_id();
        assert!(id.starts_with("db-"));
        assert!(id.len() > 3);
    }

    #[test]
    fn default_engine_versions_list_not_empty() {
        let versions = default_engine_versions();
        assert!(!versions.is_empty());
    }

    #[test]
    fn default_orderable_options_list_not_empty() {
        let opts = default_orderable_options();
        assert!(!opts.is_empty());
    }

    #[test]
    fn default_parameter_groups_returned_per_family() {
        let groups = default_parameter_groups("123456789012", "us-east-1");
        assert!(!groups.is_empty());
    }

    fn make_instance(id: &str) -> DbInstance {
        let created_at = Utc::now();
        DbInstance {
            db_instance_identifier: id.to_string(),
            db_instance_arn: format!("arn:aws:rds:us-east-1:123:db:{id}"),
            db_instance_class: "db.t3.micro".to_string(),
            engine: "postgres".to_string(),
            engine_version: "16.3".to_string(),
            db_instance_status: "available".to_string(),
            master_username: "admin".to_string(),
            db_name: None,
            endpoint_address: "x".to_string(),
            port: 5432,
            allocated_storage: 20,
            publicly_accessible: false,
            deletion_protection: false,
            created_at,
            dbi_resource_id: "d".to_string(),
            master_user_password: "p".to_string(),
            container_id: "c".to_string(),
            host_port: 0,
            tags: Vec::new(),
            read_replica_source_db_instance_identifier: None,
            read_replica_db_instance_identifiers: Vec::new(),
            vpc_security_group_ids: Vec::new(),
            db_parameter_group_name: None,
            backup_retention_period: 0,
            preferred_backup_window: String::new(),
            latest_restorable_time: None,
            option_group_name: None,
            multi_az: false,
            pending_modified_values: None,
        }
    }

    #[test]
    fn finish_instance_creation_moves_from_pending_to_instances() {
        let mut state = RdsState::new("123456789012", "us-east-1");
        assert!(state.begin_instance_creation("db-x"));
        assert!(state.in_progress_instance_ids.contains("db-x"));
        state.finish_instance_creation(make_instance("db-x"));
        assert!(!state.in_progress_instance_ids.contains("db-x"));
        assert!(state.instances.contains_key("db-x"));
    }

    #[test]
    fn cancel_instance_creation_drops_pending() {
        let mut state = RdsState::new("123456789012", "us-east-1");
        state.begin_instance_creation("db-y");
        state.cancel_instance_creation("db-y");
        assert!(!state.in_progress_instance_ids.contains("db-y"));
    }

    #[test]
    fn begin_instance_creation_rejects_when_already_created() {
        let mut state = RdsState::new("123456789012", "us-east-1");
        state
            .instances
            .insert("db-z".to_string(), make_instance("db-z"));
        assert!(!state.begin_instance_creation("db-z"));
    }

    #[test]
    fn reset_restores_default_parameter_groups() {
        let mut state = RdsState::new("123456789012", "us-east-1");
        state.parameter_groups.clear();
        state.reset();
        assert!(!state.parameter_groups.is_empty());
    }

    #[test]
    fn arn_helpers_include_region_and_account() {
        let state = RdsState::new("111122223333", "ap-southeast-2");
        let arn = state.db_instance_arn("my-db");
        assert!(arn.contains("111122223333"));
        assert!(arn.contains("ap-southeast-2"));
        let snap = state.db_snapshot_arn("snap");
        assert!(snap.contains("snapshot:snap"));
    }

    #[test]
    fn next_dbi_resource_id_unique_across_calls() {
        let state = RdsState::new("123", "us-east-1");
        let a = state.next_dbi_resource_id();
        let b = state.next_dbi_resource_id();
        assert_ne!(a, b);
    }
}
