//! DNS metadata storage using FlareDB, PostgreSQL, or SQLite.

use dashmap::DashMap;
use flaredb_client::RdbClient;
use flashdns_types::{cidr_to_arpa, Record, RecordId, RecordType, ReverseZone, Zone, ZoneId};
use sqlx::pool::PoolOptions;
use sqlx::{Pool, Postgres, Sqlite};
use std::sync::Arc;
use tokio::sync::Mutex;

/// Result type for metadata operations
pub type Result<T> = std::result::Result<T, MetadataError>;

/// Metadata operation error
#[derive(Debug, thiserror::Error)]
pub enum MetadataError {
    #[error("Storage error: {0}")]
    Storage(String),
    #[error("Serialization error: {0}")]
    Serialization(String),
    #[error("Not found: {0}")]
    NotFound(String),
    #[error("Invalid argument: {0}")]
    InvalidArgument(String),
}

/// Storage backend enum
enum StorageBackend {
    FlareDB(Arc<Mutex<RdbClient>>),
    Sql(SqlStorageBackend),
    InMemory(Arc<DashMap<String, String>>),
}

enum SqlStorageBackend {
    Postgres(Arc<Pool<Postgres>>),
    Sqlite(Arc<Pool<Sqlite>>),
}

/// DNS Metadata store for zones and records
pub struct DnsMetadataStore {
    backend: StorageBackend,
}

impl DnsMetadataStore {
    /// Create a new metadata store with FlareDB backend
    pub async fn new(endpoint: Option<String>) -> Result<Self> {
        Self::new_flaredb(endpoint).await
    }

    /// Create a new metadata store with FlareDB backend
    pub async fn new_flaredb(endpoint: Option<String>) -> Result<Self> {
        Self::new_flaredb_with_pd(endpoint, None).await
    }

    /// Create a new metadata store with FlareDB backend and explicit PD address.
    pub async fn new_flaredb_with_pd(
        endpoint: Option<String>,
        pd_endpoint: Option<String>,
    ) -> Result<Self> {
        let endpoint = endpoint.unwrap_or_else(|| "127.0.0.1:2479".to_string());
        let pd_endpoint = pd_endpoint
            .map(|value| normalize_transport_addr(&value))
            .unwrap_or_else(|| endpoint.clone());

        let client = RdbClient::connect_with_pd_namespace(endpoint, pd_endpoint, "flashdns")
            .await
            .map_err(|e| MetadataError::Storage(format!("Failed to connect to FlareDB: {}", e)))?;

        Ok(Self {
            backend: StorageBackend::FlareDB(Arc::new(Mutex::new(client))),
        })
    }

    /// Create a metadata store backed by PostgreSQL or SQLite.
    pub async fn new_sql(database_url: &str, single_node: bool) -> Result<Self> {
        let url = database_url.trim();
        if url.is_empty() {
            return Err(MetadataError::InvalidArgument(
                "metadata database URL is empty".to_string(),
            ));
        }

        if Self::is_postgres_url(url) {
            let pool = PoolOptions::<Postgres>::new()
                .max_connections(10)
                .connect(url)
                .await
                .map_err(|e| {
                    MetadataError::Storage(format!("Failed to connect to Postgres: {}", e))
                })?;
            Self::ensure_sql_schema_postgres(&pool).await?;
            return Ok(Self {
                backend: StorageBackend::Sql(SqlStorageBackend::Postgres(Arc::new(pool))),
            });
        }

        if Self::is_sqlite_url(url) {
            if !single_node {
                return Err(MetadataError::InvalidArgument(
                    "SQLite is allowed only in single-node mode".to_string(),
                ));
            }
            if url.contains(":memory:") {
                return Err(MetadataError::InvalidArgument(
                    "In-memory SQLite is not allowed".to_string(),
                ));
            }
            let pool = PoolOptions::<Sqlite>::new()
                .max_connections(1)
                .connect(url)
                .await
                .map_err(|e| {
                    MetadataError::Storage(format!("Failed to connect to SQLite: {}", e))
                })?;
            Self::ensure_sql_schema_sqlite(&pool).await?;
            return Ok(Self {
                backend: StorageBackend::Sql(SqlStorageBackend::Sqlite(Arc::new(pool))),
            });
        }

        Err(MetadataError::InvalidArgument(
            "Unsupported metadata database URL (use postgres://, postgresql://, or sqlite:)"
                .to_string(),
        ))
    }

    /// Create a new in-memory metadata store (for testing)
    pub fn new_in_memory() -> Self {
        Self {
            backend: StorageBackend::InMemory(Arc::new(DashMap::new())),
        }
    }

    fn is_postgres_url(url: &str) -> bool {
        url.starts_with("postgres://") || url.starts_with("postgresql://")
    }

    fn is_sqlite_url(url: &str) -> bool {
        url.starts_with("sqlite:")
    }

    async fn ensure_sql_schema_postgres(pool: &Pool<Postgres>) -> Result<()> {
        sqlx::query(
            "CREATE TABLE IF NOT EXISTS metadata_kv (
                key TEXT PRIMARY KEY,
                value TEXT NOT NULL
            )",
        )
        .execute(pool)
        .await
        .map_err(|e| {
            MetadataError::Storage(format!("Failed to initialize Postgres schema: {}", e))
        })?;
        Ok(())
    }

    async fn ensure_sql_schema_sqlite(pool: &Pool<Sqlite>) -> Result<()> {
        sqlx::query(
            "CREATE TABLE IF NOT EXISTS metadata_kv (
                key TEXT PRIMARY KEY,
                value TEXT NOT NULL
            )",
        )
        .execute(pool)
        .await
        .map_err(|e| {
            MetadataError::Storage(format!("Failed to initialize SQLite schema: {}", e))
        })?;
        Ok(())
    }

    // =========================================================================
    // Internal storage helpers
    // =========================================================================

    async fn put(&self, key: &str, value: &str) -> Result<()> {
        match &self.backend {
            StorageBackend::FlareDB(client) => {
                let mut c = client.lock().await;
                c.raw_put(key.as_bytes().to_vec(), value.as_bytes().to_vec())
                    .await
                    .map_err(|e| MetadataError::Storage(format!("FlareDB put failed: {}", e)))?;
            }
            StorageBackend::Sql(sql) => match sql {
                SqlStorageBackend::Postgres(pool) => {
                    sqlx::query(
                        "INSERT INTO metadata_kv (key, value)
                         VALUES ($1, $2)
                         ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value",
                    )
                    .bind(key)
                    .bind(value)
                    .execute(pool.as_ref())
                    .await
                    .map_err(|e| MetadataError::Storage(format!("Postgres put failed: {}", e)))?;
                }
                SqlStorageBackend::Sqlite(pool) => {
                    sqlx::query(
                        "INSERT INTO metadata_kv (key, value)
                         VALUES (?1, ?2)
                         ON CONFLICT(key) DO UPDATE SET value = excluded.value",
                    )
                    .bind(key)
                    .bind(value)
                    .execute(pool.as_ref())
                    .await
                    .map_err(|e| MetadataError::Storage(format!("SQLite put failed: {}", e)))?;
                }
            },
            StorageBackend::InMemory(map) => {
                map.insert(key.to_string(), value.to_string());
            }
        }
        Ok(())
    }

    async fn get(&self, key: &str) -> Result<Option<String>> {
        match &self.backend {
            StorageBackend::FlareDB(client) => {
                let mut c = client.lock().await;
                let result = c
                    .raw_get(key.as_bytes().to_vec())
                    .await
                    .map_err(|e| MetadataError::Storage(format!("FlareDB get failed: {}", e)))?;
                Ok(result.map(|bytes| String::from_utf8_lossy(&bytes).to_string()))
            }
            StorageBackend::Sql(sql) => match sql {
                SqlStorageBackend::Postgres(pool) => {
                    let value: Option<String> =
                        sqlx::query_scalar("SELECT value FROM metadata_kv WHERE key = $1")
                            .bind(key)
                            .fetch_optional(pool.as_ref())
                            .await
                            .map_err(|e| {
                                MetadataError::Storage(format!("Postgres get failed: {}", e))
                            })?;
                    Ok(value)
                }
                SqlStorageBackend::Sqlite(pool) => {
                    let value: Option<String> =
                        sqlx::query_scalar("SELECT value FROM metadata_kv WHERE key = ?1")
                            .bind(key)
                            .fetch_optional(pool.as_ref())
                            .await
                            .map_err(|e| {
                                MetadataError::Storage(format!("SQLite get failed: {}", e))
                            })?;
                    Ok(value)
                }
            },
            StorageBackend::InMemory(map) => Ok(map.get(key).map(|v| v.value().clone())),
        }
    }

    async fn delete_key(&self, key: &str) -> Result<()> {
        match &self.backend {
            StorageBackend::FlareDB(client) => {
                let mut c = client.lock().await;
                c.raw_delete(key.as_bytes().to_vec())
                    .await
                    .map_err(|e| MetadataError::Storage(format!("FlareDB delete failed: {}", e)))?;
            }
            StorageBackend::Sql(sql) => match sql {
                SqlStorageBackend::Postgres(pool) => {
                    sqlx::query("DELETE FROM metadata_kv WHERE key = $1")
                        .bind(key)
                        .execute(pool.as_ref())
                        .await
                        .map_err(|e| {
                            MetadataError::Storage(format!("Postgres delete failed: {}", e))
                        })?;
                }
                SqlStorageBackend::Sqlite(pool) => {
                    sqlx::query("DELETE FROM metadata_kv WHERE key = ?1")
                        .bind(key)
                        .execute(pool.as_ref())
                        .await
                        .map_err(|e| {
                            MetadataError::Storage(format!("SQLite delete failed: {}", e))
                        })?;
                }
            },
            StorageBackend::InMemory(map) => {
                map.remove(key);
            }
        }
        Ok(())
    }

    async fn get_prefix(&self, prefix: &str) -> Result<Vec<(String, String)>> {
        match &self.backend {
            StorageBackend::FlareDB(client) => {
                let mut c = client.lock().await;

                // Calculate end_key by incrementing the last byte of prefix
                let mut end_key = prefix.as_bytes().to_vec();
                if let Some(last) = end_key.last_mut() {
                    if *last == 0xff {
                        // If last byte is 0xff, append a 0x00
                        end_key.push(0x00);
                    } else {
                        *last += 1;
                    }
                } else {
                    // Empty prefix - scan everything
                    end_key.push(0xff);
                }

                let mut results = Vec::new();
                let mut start_key = prefix.as_bytes().to_vec();

                // Pagination loop to get all results
                loop {
                    let (keys, values, next) = c
                        .raw_scan(
                            start_key.clone(),
                            end_key.clone(),
                            1000, // Batch size
                        )
                        .await
                        .map_err(|e| {
                            MetadataError::Storage(format!("FlareDB scan failed: {}", e))
                        })?;

                    // Convert and add results
                    for (k, v) in keys.iter().zip(values.iter()) {
                        results.push((
                            String::from_utf8_lossy(k).to_string(),
                            String::from_utf8_lossy(v).to_string(),
                        ));
                    }

                    // Check if there are more results
                    if let Some(next_key) = next {
                        start_key = next_key;
                    } else {
                        break;
                    }
                }

                Ok(results)
            }
            StorageBackend::Sql(sql) => {
                let like_pattern = format!("{}%", prefix);
                match sql {
                    SqlStorageBackend::Postgres(pool) => {
                        let rows: Vec<(String, String)> = sqlx::query_as(
                            "SELECT key, value FROM metadata_kv WHERE key LIKE $1 ORDER BY key",
                        )
                        .bind(like_pattern)
                        .fetch_all(pool.as_ref())
                        .await
                        .map_err(|e| {
                            MetadataError::Storage(format!("Postgres scan failed: {}", e))
                        })?;
                        Ok(rows)
                    }
                    SqlStorageBackend::Sqlite(pool) => {
                        let rows: Vec<(String, String)> = sqlx::query_as(
                            "SELECT key, value FROM metadata_kv WHERE key LIKE ?1 ORDER BY key",
                        )
                        .bind(like_pattern)
                        .fetch_all(pool.as_ref())
                        .await
                        .map_err(|e| {
                            MetadataError::Storage(format!("SQLite scan failed: {}", e))
                        })?;
                        Ok(rows)
                    }
                }
            }
            StorageBackend::InMemory(map) => {
                let mut results = Vec::new();
                for entry in map.iter() {
                    if entry.key().starts_with(prefix) {
                        results.push((entry.key().clone(), entry.value().clone()));
                    }
                }
                Ok(results)
            }
        }
    }

    // =========================================================================
    // Key builders
    // =========================================================================

    fn zone_key(org_id: &str, project_id: &str, zone_name: &str) -> String {
        format!("/flashdns/zones/{}/{}/{}", org_id, project_id, zone_name)
    }

    fn zone_id_key(zone_id: &ZoneId) -> String {
        format!("/flashdns/zone_ids/{}", zone_id)
    }

    fn record_key(
        zone_id: &ZoneId,
        record_name: &str,
        record_type: RecordType,
        record_id: &RecordId,
    ) -> String {
        format!(
            "/flashdns/records/{}/{}/{}/{}",
            zone_id, record_name, record_type, record_id
        )
    }

    fn record_prefix(zone_id: &ZoneId) -> String {
        format!("/flashdns/records/{}/", zone_id)
    }

    fn record_id_key(record_id: &RecordId) -> String {
        format!("/flashdns/record_ids/{}", record_id)
    }

    // =========================================================================
    // Zone operations
    // =========================================================================

    /// Save zone metadata
    pub async fn save_zone(&self, zone: &Zone) -> Result<()> {
        let key = Self::zone_key(&zone.org_id, &zone.project_id, zone.name.as_str());
        let value = serde_json::to_string(zone).map_err(|e| {
            MetadataError::Serialization(format!("Failed to serialize zone: {}", e))
        })?;

        self.put(&key, &value).await?;

        // Also save zone ID mapping
        let id_key = Self::zone_id_key(&zone.id);
        self.put(&id_key, &key).await?;

        Ok(())
    }

    /// Load zone by name
    pub async fn load_zone(
        &self,
        org_id: &str,
        project_id: &str,
        zone_name: &str,
    ) -> Result<Option<Zone>> {
        let key = Self::zone_key(org_id, project_id, zone_name);

        if let Some(value) = self.get(&key).await? {
            let zone: Zone = serde_json::from_str(&value).map_err(|e| {
                MetadataError::Serialization(format!("Failed to deserialize zone: {}", e))
            })?;
            Ok(Some(zone))
        } else {
            Ok(None)
        }
    }

    /// Load zone by ID
    pub async fn load_zone_by_id(&self, zone_id: &ZoneId) -> Result<Option<Zone>> {
        let id_key = Self::zone_id_key(zone_id);

        if let Some(zone_key) = self.get(&id_key).await? {
            if let Some(value) = self.get(&zone_key).await? {
                let zone: Zone = serde_json::from_str(&value).map_err(|e| {
                    MetadataError::Serialization(format!("Failed to deserialize zone: {}", e))
                })?;
                Ok(Some(zone))
            } else {
                Ok(None)
            }
        } else {
            Ok(None)
        }
    }

    /// Delete zone (cascade delete all records)
    pub async fn delete_zone(&self, zone: &Zone) -> Result<()> {
        // First, delete all records in the zone (cascade delete)
        self.delete_zone_records(&zone.id).await?;

        // Then delete the zone metadata
        let key = Self::zone_key(&zone.org_id, &zone.project_id, zone.name.as_str());
        let id_key = Self::zone_id_key(&zone.id);

        self.delete_key(&key).await?;
        self.delete_key(&id_key).await?;

        Ok(())
    }

    /// List zones for a tenant
    pub async fn list_zones(&self, org_id: &str, project_id: Option<&str>) -> Result<Vec<Zone>> {
        let prefix = if org_id == "*" {
            "/flashdns/zones/".to_string()
        } else if let Some(project_id) = project_id {
            format!("/flashdns/zones/{}/{}/", org_id, project_id)
        } else {
            format!("/flashdns/zones/{}/", org_id)
        };

        let items = self.get_prefix(&prefix).await?;

        let mut zones = Vec::new();
        for (_, value) in items {
            if let Ok(zone) = serde_json::from_str::<Zone>(&value) {
                if org_id != "*" && zone.org_id != org_id {
                    continue;
                }
                if let Some(project_id) = project_id {
                    if zone.project_id != project_id {
                        continue;
                    }
                }
                zones.push(zone);
            }
        }

        // Sort by name for consistent ordering
        zones.sort_by(|a, b| a.name.as_str().cmp(b.name.as_str()));

        Ok(zones)
    }

    // =========================================================================
    // Record operations
    // =========================================================================

    /// Save record
    pub async fn save_record(&self, record: &Record) -> Result<()> {
        let key = Self::record_key(
            &record.zone_id,
            &record.name,
            record.record_type,
            &record.id,
        );
        let id_key = Self::record_id_key(&record.id);
        if let Some(existing_key) = self.get(&id_key).await? {
            if existing_key != key {
                self.delete_key(&existing_key).await?;
            }
        }
        let value = serde_json::to_string(record).map_err(|e| {
            MetadataError::Serialization(format!("Failed to serialize record: {}", e))
        })?;

        self.put(&key, &value).await?;

        // Also save record ID mapping
        self.put(&id_key, &key).await?;

        Ok(())
    }

    /// Load record by ID
    pub async fn load_record_by_id(&self, record_id: &RecordId) -> Result<Option<Record>> {
        let id_key = Self::record_id_key(record_id);

        if let Some(record_key) = self.get(&id_key).await? {
            if let Some(value) = self.get(&record_key).await? {
                let record: Record = serde_json::from_str(&value).map_err(|e| {
                    MetadataError::Serialization(format!("Failed to deserialize record: {}", e))
                })?;
                Ok(Some(record))
            } else {
                Ok(None)
            }
        } else {
            Ok(None)
        }
    }

    /// Delete record
    pub async fn delete_record(&self, record: &Record) -> Result<()> {
        let id_key = Self::record_id_key(&record.id);
        if let Some(key) = self.get(&id_key).await? {
            self.delete_key(&key).await?;
        } else {
            self.delete_key(&Self::record_key(
                &record.zone_id,
                &record.name,
                record.record_type,
                &record.id,
            ))
            .await?;
        }
        self.delete_key(&id_key).await?;

        Ok(())
    }

    /// List records for a zone
    pub async fn list_records(&self, zone_id: &ZoneId) -> Result<Vec<Record>> {
        let prefix = Self::record_prefix(zone_id);

        let items = self.get_prefix(&prefix).await?;

        let mut records = Vec::new();
        for (_, value) in items {
            if let Ok(record) = serde_json::from_str::<Record>(&value) {
                records.push(record);
            }
        }

        // Sort by name then type for consistent ordering
        records.sort_by(|a, b| {
            a.name
                .cmp(&b.name)
                .then(a.record_type.type_code().cmp(&b.record_type.type_code()))
                .then(a.id.to_string().cmp(&b.id.to_string()))
        });

        Ok(records)
    }

    /// List records by name (all types)
    pub async fn list_records_by_name(&self, zone_id: &ZoneId, name: &str) -> Result<Vec<Record>> {
        let prefix = format!("/flashdns/records/{}/{}/", zone_id, name);

        let items = self.get_prefix(&prefix).await?;

        let mut records = Vec::new();
        for (_, value) in items {
            if let Ok(record) = serde_json::from_str::<Record>(&value) {
                records.push(record);
            }
        }

        Ok(records)
    }

    /// Delete all records for a zone
    pub async fn delete_zone_records(&self, zone_id: &ZoneId) -> Result<()> {
        let records = self.list_records(zone_id).await?;
        for record in records {
            self.delete_record(&record).await?;
        }
        Ok(())
    }

    // =========================================================================
    // Reverse Zone operations
    // =========================================================================

    /// Create a reverse zone
    pub async fn create_reverse_zone(&self, mut zone: ReverseZone) -> Result<ReverseZone> {
        // Generate arpa zone from CIDR
        zone.arpa_zone = cidr_to_arpa(&zone.cidr).map_err(|e| {
            MetadataError::InvalidArgument(format!("Failed to generate arpa zone: {}", e))
        })?;

        let zone_key = format!(
            "/flashdns/reverse_zones/{}/{}/{}",
            zone.org_id,
            zone.project_id.as_deref().unwrap_or("global"),
            zone.id
        );
        let cidr_index_key = format!(
            "/flashdns/reverse_zones/by-cidr/{}",
            normalize_cidr(&zone.cidr)
        );

        let value = serde_json::to_string(&zone).map_err(|e| {
            MetadataError::Serialization(format!("Failed to serialize reverse zone: {}", e))
        })?;

        self.put(&zone_key, &value).await?;
        self.put(&cidr_index_key, &zone.id).await?;

        Ok(zone)
    }

    /// Get a reverse zone by ID
    pub async fn get_reverse_zone(&self, zone_id: &str) -> Result<Option<ReverseZone>> {
        // Need to scan for the zone since we don't know org_id/project_id
        let prefix = "/flashdns/reverse_zones/";
        let results = self.get_prefix(prefix).await?;

        for (key, value) in results {
            if key.ends_with(&format!("/{}", zone_id)) {
                let zone: ReverseZone = serde_json::from_str(&value).map_err(|e| {
                    MetadataError::Serialization(format!(
                        "Failed to deserialize reverse zone: {}",
                        e
                    ))
                })?;
                return Ok(Some(zone));
            }
        }
        Ok(None)
    }

    /// Delete a reverse zone
    pub async fn delete_reverse_zone(&self, zone: &ReverseZone) -> Result<()> {
        let zone_key = format!(
            "/flashdns/reverse_zones/{}/{}/{}",
            zone.org_id,
            zone.project_id.as_deref().unwrap_or("global"),
            zone.id
        );
        let cidr_index_key = format!(
            "/flashdns/reverse_zones/by-cidr/{}",
            normalize_cidr(&zone.cidr)
        );

        self.delete_key(&zone_key).await?;
        self.delete_key(&cidr_index_key).await?;

        Ok(())
    }

    /// List reverse zones for an organization
    pub async fn list_reverse_zones(
        &self,
        org_id: &str,
        project_id: Option<&str>,
    ) -> Result<Vec<ReverseZone>> {
        let prefix = if org_id == "*" {
            "/flashdns/reverse_zones/".to_string()
        } else {
            format!(
                "/flashdns/reverse_zones/{}/{}/",
                org_id,
                project_id.unwrap_or("global")
            )
        };
        let results = self.get_prefix(&prefix).await?;

        let mut zones = Vec::new();
        for (_, value) in results {
            if let Ok(zone) = serde_json::from_str::<ReverseZone>(&value) {
                if org_id != "*" && zone.org_id != org_id {
                    continue;
                }
                if let Some(project_id) = project_id {
                    if zone.project_id.as_deref().unwrap_or("global") != project_id {
                        continue;
                    }
                }
                zones.push(zone);
            }
        }
        Ok(zones)
    }
}

/// Normalize CIDR for use as key (replace / with _, . with -, : with -)
fn normalize_cidr(cidr: &str) -> String {
    cidr.replace('/', "_").replace(['.', ':'], "-")
}

fn normalize_transport_addr(endpoint: &str) -> String {
    endpoint
        .trim()
        .trim_start_matches("http://")
        .trim_start_matches("https://")
        .trim_end_matches('/')
        .to_string()
}

#[cfg(test)]
mod tests {
    use super::*;
    use flashdns_types::{RecordData, ZoneName};

    #[tokio::test]
    async fn test_zone_crud() {
        let store = DnsMetadataStore::new_in_memory();

        let zone_name = ZoneName::new("example.com").unwrap();
        let zone = Zone::new(zone_name, "test-org", "test-project");

        // Save
        store.save_zone(&zone).await.unwrap();

        // Load by name
        let loaded = store
            .load_zone("test-org", "test-project", "example.com.")
            .await
            .unwrap()
            .unwrap();
        assert_eq!(loaded.id, zone.id);

        // Load by ID
        let loaded_by_id = store.load_zone_by_id(&zone.id).await.unwrap().unwrap();
        assert_eq!(loaded_by_id.name.as_str(), "example.com.");

        // List
        let zones = store.list_zones("test-org", None).await.unwrap();
        assert_eq!(zones.len(), 1);
        let wildcard_zones = store.list_zones("*", None).await.unwrap();
        assert_eq!(wildcard_zones.len(), 1);
        assert_eq!(wildcard_zones[0].id, zone.id);

        // Delete
        store.delete_zone(&zone).await.unwrap();
        let deleted = store
            .load_zone("test-org", "test-project", "example.com.")
            .await
            .unwrap();
        assert!(deleted.is_none());
    }

    #[tokio::test]
    async fn test_record_crud() {
        let store = DnsMetadataStore::new_in_memory();

        let zone_name = ZoneName::new("example.com").unwrap();
        let zone = Zone::new(zone_name, "test-org", "test-project");
        store.save_zone(&zone).await.unwrap();

        // Create A record
        let record_data = RecordData::a_from_str("192.168.1.1").unwrap();
        let record = Record::new(zone.id, "www", record_data);

        // Save
        store.save_record(&record).await.unwrap();

        // List
        let records = store.list_records_by_name(&zone.id, "www").await.unwrap();
        assert_eq!(records.len(), 1);
        assert_eq!(records[0].id, record.id);

        // Delete
        store.delete_record(&record).await.unwrap();
        let deleted = store.list_records_by_name(&zone.id, "www").await.unwrap();
        assert!(deleted.is_empty());
    }

    #[tokio::test]
    async fn test_record_set_supports_multiple_records_with_same_name_and_type() {
        let store = DnsMetadataStore::new_in_memory();

        let zone_name = ZoneName::new("example.com").unwrap();
        let zone = Zone::new(zone_name, "test-org", "test-project");
        store.save_zone(&zone).await.unwrap();

        let first = Record::new(
            zone.id,
            "api",
            RecordData::a_from_str("192.168.1.10").unwrap(),
        );
        let second = Record::new(
            zone.id,
            "api",
            RecordData::a_from_str("192.168.1.11").unwrap(),
        );

        store.save_record(&first).await.unwrap();
        store.save_record(&second).await.unwrap();

        let records = store.list_records_by_name(&zone.id, "api").await.unwrap();
        assert_eq!(records.len(), 2);
        assert!(records.iter().any(|record| record.id == first.id));
        assert!(records.iter().any(|record| record.id == second.id));

        store.delete_record(&first).await.unwrap();

        let remaining = store.list_records_by_name(&zone.id, "api").await.unwrap();
        assert_eq!(remaining.len(), 1);
        assert_eq!(remaining[0].id, second.id);
    }
}
