//! Synor Hosting - Decentralized Web Hosting
//!
//! Provides subdomain-based hosting for web applications on Synor Storage.
//!
//! # Components
//!
//! - **Name Registry**: On-chain mapping of names to CIDs
//! - **Domain Verification**: Custom domain ownership proof
//! - **Gateway Router**: Host-based routing to content
//!
//! # Example
//!
//! ```rust,ignore
//! use synor_hosting::{NameRegistry, HostingGateway};
//!
//! // Register a name
//! registry.register("myapp", cid, owner)?;
//!
//! // Resolve name to CID
//! let cid = registry.resolve("myapp")?;
//!
//! // Gateway routes myapp.synor.network to CID
//! gateway.handle_request("myapp.synor.network", "/").await?;
//! ```

pub mod registry;
pub mod domain;
pub mod router;
pub mod config;
pub mod error;

#[cfg(feature = "server")]
pub mod server;

pub use registry::{NameRegistry, NameRecord, RegistrationRequest};
pub use domain::{DomainVerifier, DomainRecord, VerificationMethod};
pub use router::{HostingRouter, RouteConfig};
pub use config::SynorJson;
pub use error::{Error, Result};

#[cfg(feature = "server")]
pub use server::{HostingGateway, GatewayConfig};

/// Reserved names that cannot be registered
pub const RESERVED_NAMES: &[&str] = &[
    "synor", "admin", "api", "gateway", "www", "mail", "ftp", "ssh",
    "cdn", "static", "assets", "app", "web", "blog", "docs", "help",
    "support", "status", "system", "root", "test", "dev", "staging",
    "prod", "production", "null", "undefined", "localhost", "local",
];

/// Validate a name according to registry rules
pub fn validate_name(name: &str) -> Result<()> {
    // Length check
    if name.len() < 3 {
        return Err(Error::InvalidName("Name must be at least 3 characters".into()));
    }
    if name.len() > 63 {
        return Err(Error::InvalidName("Name must be at most 63 characters".into()));
    }

    // Character check
    if !name.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-') {
        return Err(Error::InvalidName(
            "Name must contain only lowercase letters, numbers, and hyphens".into(),
        ));
    }

    // Cannot start or end with hyphen
    if name.starts_with('-') || name.ends_with('-') {
        return Err(Error::InvalidName("Name cannot start or end with hyphen".into()));
    }

    // Cannot have consecutive hyphens
    if name.contains("--") {
        return Err(Error::InvalidName("Name cannot contain consecutive hyphens".into()));
    }

    // Reserved name check
    if RESERVED_NAMES.contains(&name) {
        return Err(Error::ReservedName(name.to_string()));
    }

    Ok(())
}

/// Check if a name looks confusingly similar to another
pub fn is_confusable(name: &str, other: &str) -> bool {
    // Simple homoglyph detection
    let normalize = |s: &str| -> String {
        s.chars()
            .map(|c| match c {
                '0' => 'o',
                '1' | 'l' => 'i',
                '5' => 's',
                '3' => 'e',
                '4' => 'a',
                '7' => 't',
                '8' => 'b',
                '9' => 'g',
                _ => c,
            })
            .collect()
    };

    normalize(name) == normalize(other)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validate_name_valid() {
        assert!(validate_name("myapp").is_ok());
        assert!(validate_name("my-app").is_ok());
        assert!(validate_name("app123").is_ok());
        assert!(validate_name("123app").is_ok());
        assert!(validate_name("a-b-c").is_ok());
    }

    #[test]
    fn test_validate_name_invalid() {
        assert!(validate_name("ab").is_err()); // Too short
        assert!(validate_name("MyApp").is_err()); // Uppercase
        assert!(validate_name("-myapp").is_err()); // Starts with hyphen
        assert!(validate_name("myapp-").is_err()); // Ends with hyphen
        assert!(validate_name("my--app").is_err()); // Consecutive hyphens
        assert!(validate_name("my app").is_err()); // Space
        assert!(validate_name("my_app").is_err()); // Underscore
    }

    #[test]
    fn test_reserved_names() {
        assert!(validate_name("synor").is_err());
        assert!(validate_name("admin").is_err());
        assert!(validate_name("api").is_err());
        assert!(validate_name("gateway").is_err());
    }

    #[test]
    fn test_confusable_names() {
        assert!(is_confusable("g00gle", "google"));
        assert!(is_confusable("paypa1", "paypal"));
        assert!(is_confusable("fac3book", "facebook"));
        assert!(!is_confusable("myapp", "yourapp"));
    }
}
