use {
    async_trait::async_trait,
    icann_rdap_common::response::{
        Autnum, Domain, Entity, Help, Nameserver, Network, RdapResponse, Rfc9083Error,
    },
};

use crate::error::RdapServerError;

use self::data::{AutnumId, DomainId, EntityId, NameserverId, NetworkId};

pub mod data;
pub mod mem;
pub mod pg;

pub type DynStoreOps = dyn StoreOps + Send + Sync;

/// This trait defines the operations for a storage engine.
#[async_trait]
pub trait StoreOps: Send + Sync {
    /// Initializes the backend storage
    async fn init(&self) -> Result<(), RdapServerError>;

    /// Gets a new transaction.
    async fn new_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError>;

    /// Gets a new transaction in which all the previous data has been truncated (cleared).
    async fn new_truncate_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError>;

    /// Get a domain from storage using the 'ldhName' as the key.
    async fn get_domain_by_ldh(&self, ldh: &str) -> Result<RdapResponse, RdapServerError>;

    /// Get a domain from storage using the 'unicodeName' as the key.
    async fn get_domain_by_unicode(&self, unicode: &str) -> Result<RdapResponse, RdapServerError>;

    /// Get an entity from storage using the 'handle' of the entity as the key.
    async fn get_entity_by_handle(&self, handle: &str) -> Result<RdapResponse, RdapServerError>;

    /// Get a nameserver from storage using the 'ldhName' as the key.
    async fn get_nameserver_by_ldh(&self, ldh: &str) -> Result<RdapResponse, RdapServerError>;

    /// Get an autnum from storage using an autonomous system numbers as the key.
    async fn get_autnum_by_num(&self, num: u32) -> Result<RdapResponse, RdapServerError>;

    /// Get a network from storage using an IP address. The network returned should be the
    /// most specific (longest prefix) network containing the IP address.
    async fn get_network_by_ipaddr(&self, ipaddr: &str) -> Result<RdapResponse, RdapServerError>;

    /// Get a network from storage using a CIDR notation network (e.g. "10.0.0.0/8"). The IP address
    /// portion of the CIDR should be assumed to be complete, that is not "10.0/8". The network
    /// returned should be the most specific (longest prefix) network containing the IP address.
    async fn get_network_by_cidr(&self, cidr: &str) -> Result<RdapResponse, RdapServerError>;

    /// Get server help.
    async fn get_srv_help(&self, host: Option<&str>) -> Result<RdapResponse, RdapServerError>;

    /// Search for domains by name.
    async fn search_domains_by_name(&self, name: &str) -> Result<RdapResponse, RdapServerError>;

    /// Search for nameservers by name.
    async fn search_nameservers_by_name(&self, name: &str)
    -> Result<RdapResponse, RdapServerError>;

    /// Search for nameservers by IP address.
    async fn search_nameservers_by_ip(
        &self,
        ip: std::net::IpAddr,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for domains by nameserver IP address.
    async fn search_domains_by_ns_ip(
        &self,
        ip: std::net::IpAddr,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for domains by nameserver ldhName.
    async fn search_domains_by_ns_ldh_name(
        &self,
        name: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for entities by handle.
    async fn search_entities_by_handle(
        &self,
        handle: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for entities by full name.
    async fn search_entities_by_full_name(
        &self,
        full_name: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for networks by handle.
    async fn search_networks_by_handle(
        &self,
        handle: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for networks by name (RFC 9910).
    async fn search_networks_by_name(&self, name: &str) -> Result<RdapResponse, RdapServerError>;

    /// Search for the supernet (rdap-up per RFC 9910) by IP address.
    async fn search_ip_rdap_up_by_ipaddr(
        &self,
        ipaddr: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for the supernet (rdap-up per RFC 9910) by CIDR.
    async fn search_ip_rdap_up_by_cidr(&self, cidr: &str) -> Result<RdapResponse, RdapServerError>;

    /// Search for the topmost containing network (rdap-top per RFC 9910) by IP address.
    async fn search_ip_rdap_top_by_ipaddr(
        &self,
        ipaddr: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for the topmost containing network (rdap-top per RFC 9910) by CIDR.
    async fn search_ip_rdap_top_by_cidr(&self, cidr: &str)
    -> Result<RdapResponse, RdapServerError>;

    /// Search for immediate child networks (rdap-down per RFC 9910) by IP address.
    async fn search_ip_rdap_down_by_ipaddr(
        &self,
        ipaddr: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for immediate child networks (rdap-down per RFC 9910) by CIDR.
    async fn search_ip_rdap_down_by_cidr(
        &self,
        cidr: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for leaf networks (rdap-bottom per RFC 9910) by IP address.
    async fn search_ip_rdap_bottom_by_ipaddr(
        &self,
        ipaddr: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for leaf networks (rdap-bottom per RFC 9910) by CIDR.
    async fn search_ip_rdap_bottom_by_cidr(
        &self,
        cidr: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for autnums by handle (RFC 9910).
    async fn search_autnums_by_handle(&self, handle: &str)
    -> Result<RdapResponse, RdapServerError>;

    /// Search for autnums by name (RFC 9910).
    async fn search_autnums_by_name(&self, name: &str) -> Result<RdapResponse, RdapServerError>;

    /// Search for the supernet (rdap-up per RFC 9910) by AS number.
    async fn search_autnum_rdap_up_by_num(&self, num: u32)
    -> Result<RdapResponse, RdapServerError>;

    /// Search for the supernet (rdap-up per RFC 9910) by AS range.
    async fn search_autnum_rdap_up_by_range(
        &self,
        start: u32,
        end: u32,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for the topmost containing network (rdap-top per RFC 9910) by AS number.
    async fn search_autnum_rdap_top_by_num(
        &self,
        num: u32,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for the topmost containing network (rdap-top per RFC 9910) by AS range.
    async fn search_autnum_rdap_top_by_range(
        &self,
        start: u32,
        end: u32,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for immediate child networks (rdap-down per RFC 9910) by AS number.
    async fn search_autnum_rdap_down_by_num(
        &self,
        num: u32,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for immediate child networks (rdap-down per RFC 9910) by AS range.
    async fn search_autnum_rdap_down_by_range(
        &self,
        start: u32,
        end: u32,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for leaf networks (rdap-bottom per RFC 9910) by AS number.
    async fn search_autnum_rdap_bottom_by_num(
        &self,
        num: u32,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for leaf networks (rdap-bottom per RFC 9910) by AS range.
    async fn search_autnum_rdap_bottom_by_range(
        &self,
        start: u32,
        end: u32,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for the supernet (rdap-up per RFC 9910) for reverse DNS domains by domain name.
    async fn search_domain_rdap_up_by_ldh(
        &self,
        ldh: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for the topmost containing domain (rdap-top per RFC 9910) for reverse DNS domains by domain name.
    async fn search_domain_rdap_top_by_ldh(
        &self,
        ldh: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for immediate child domains (rdap-down per RFC 9910) for reverse DNS domains by domain name.
    async fn search_domain_rdap_down_by_ldh(
        &self,
        ldh: &str,
    ) -> Result<RdapResponse, RdapServerError>;

    /// Search for leaf domains (rdap-bottom per RFC 9910) for reverse DNS domains by domain name.
    async fn search_domain_rdap_bottom_by_ldh(
        &self,
        ldh: &str,
    ) -> Result<RdapResponse, RdapServerError>;
}

/// Represents a handle to a transaction.
/// The implementation of the transaction
/// are dependent on the storage type.
#[async_trait]
pub trait TxHandle: Send {
    /// Add a domain name to storage.
    async fn add_domain(&mut self, domain: &Domain) -> Result<(), RdapServerError>;

    /// Add an error as a domain to storage. This is useful for specifying redirects.
    async fn add_domain_err(
        &mut self,
        domain_id: &DomainId,
        error: &Rfc9083Error,
    ) -> Result<(), RdapServerError>;

    /// Add an entity to storage.
    async fn add_entity(&mut self, entity: &Entity) -> Result<(), RdapServerError>;

    /// Add an error as an entity to storage. This is useful for specifying redirects.
    async fn add_entity_err(
        &mut self,
        entity_id: &EntityId,
        error: &Rfc9083Error,
    ) -> Result<(), RdapServerError>;

    /// Add a nameserver to storage.
    async fn add_nameserver(&mut self, nameserver: &Nameserver) -> Result<(), RdapServerError>;

    /// Add an error as a nameserver to storage. This is useful for specifying redirects.
    async fn add_nameserver_err(
        &mut self,
        nameserver_id: &NameserverId,
        error: &Rfc9083Error,
    ) -> Result<(), RdapServerError>;

    /// Add a nameserver to storage.
    async fn add_autnum(&mut self, autnum: &Autnum) -> Result<(), RdapServerError>;

    /// Add an error as an autnum to storage. This is useful for specifying redirects.
    async fn add_autnum_err(
        &mut self,
        autnum_id: &AutnumId,
        error: &Rfc9083Error,
    ) -> Result<(), RdapServerError>;

    /// Add a network to storage.
    async fn add_network(&mut self, network: &Network) -> Result<(), RdapServerError>;

    /// Add a network as an autnum to storage. This is useful for specifying redirects.
    async fn add_network_err(
        &mut self,
        network_id: &NetworkId,
        error: &Rfc9083Error,
    ) -> Result<(), RdapServerError>;

    async fn add_srv_help(
        &mut self,
        help: &Help,
        host: Option<&str>,
    ) -> Result<(), RdapServerError>;

    /// Commit the transaction.
    async fn commit(self: Box<Self>) -> Result<(), RdapServerError>;

    /// Rollback the transaction.
    async fn rollback(self: Box<Self>) -> Result<(), RdapServerError>;
}
