use crate::state::DbFlavor;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionConfig {
    pub id: String,
    pub name: String,
    pub host: String,
    pub port: u16,
    pub user: String,
    pub password: String,
    pub database: String,
    pub ssl_mode: Option<String>,
    pub color: Option<String>,
    pub environment: Option<String>,
    /// Database flavor selected by the user. Defaults to PostgreSQL for backwards
    /// compatibility with older `connections.json` files written before multi-DB support.
    #[serde(default = "default_flavor")]
    pub db_flavor: DbFlavor,
    /// HTTPS for ClickHouse. Defaults to false.
    #[serde(default)]
    pub secure: bool,
}

fn default_flavor() -> DbFlavor {
    DbFlavor::PostgreSQL
}

impl ConnectionConfig {
    pub fn connection_url(&self) -> String {
        self.connection_url_for_db(&self.database)
    }

    pub fn connection_url_for_db(&self, database: &str) -> String {
        let ssl = self.ssl_mode.as_deref().unwrap_or("prefer");
        format!(
            "postgres://{}:{}@{}:{}/{}?sslmode={}",
            urlencoded(&self.user),
            urlencoded(&self.password),
            self.host,
            self.port,
            urlencoded(database),
            ssl
        )
    }
}

fn urlencoded(s: &str) -> String {
    s.chars()
        .map(|c| match c {
            ':' | '/' | '?' | '#' | '[' | ']' | '@' | '!' | '$' | '&' | '\'' | '(' | ')' | '*'
            | '+' | ',' | ';' | '=' | '%' | ' ' => {
                format!("%{:02X}", c as u8)
            }
            _ => c.to_string(),
        })
        .collect()
}
