use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum DomainStatus {
    Available,
    Registered { expiry: Option<String> },
    Error { kind: ErrorKind, message: String },
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ErrorKind {
    InvalidTld,
    Unknown,
    Timeout,
    RateLimit,
    Forbidden,
}

impl ErrorKind {
    /// parse from config string (case insensitive, underscores and hyphens both work)
    pub fn from_config_str(s: &str) -> Option<Self> {
        match s.to_lowercase().replace('-', "_").as_str() {
            "invalid_tld" | "invalidtld" => Some(ErrorKind::InvalidTld),
            "unknown" => Some(ErrorKind::Unknown),
            "timeout" => Some(ErrorKind::Timeout),
            "rate_limit" | "ratelimit" => Some(ErrorKind::RateLimit),
            "forbidden" => Some(ErrorKind::Forbidden),
            _ => None,
        }
    }

    /// back to config string
    pub fn to_config_str(&self) -> &'static str {
        match self {
            ErrorKind::InvalidTld => "invalid_tld",
            ErrorKind::Unknown => "unknown",
            ErrorKind::Timeout => "timeout",
            ErrorKind::RateLimit => "rate_limit",
            ErrorKind::Forbidden => "forbidden",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DomainResult {
    pub name: String,
    pub tld: String,
    pub full: String,
    pub status: DomainStatus,
}

impl DomainResult {
    pub fn new(name: &str, tld: &str, status: DomainStatus) -> Self {
        Self {
            name: name.to_string(),
            tld: tld.to_string(),
            full: format!("{}.{}", name, tld),
            status,
        }
    }

    pub fn is_available(&self) -> bool {
        matches!(self.status, DomainStatus::Available)
    }

    pub fn is_error(&self) -> bool {
        matches!(self.status, DomainStatus::Error { .. })
    }

    pub fn status_str(&self) -> &str {
        match &self.status {
            DomainStatus::Available => "available",
            DomainStatus::Registered { .. } => "registered",
            DomainStatus::Error { .. } => "error",
        }
    }

    pub fn note_str(&self) -> String {
        match &self.status {
            DomainStatus::Available => "-".to_string(),
            DomainStatus::Registered { expiry } => match expiry {
                Some(date) => format!("until {}", date),
                None => "no expiry info".to_string(),
            },
            DomainStatus::Error { kind, message } => match kind {
                ErrorKind::InvalidTld => "invalid tld".to_string(),
                _ => message.clone(),
            },
        }
    }
}
