use server::config::{Config, ConfigError, Environment, Protocol};
use std::fs;
use tempfile::tempdir;

#[tokio::test]
async fn test_config_loading_from_toml_file() {
    let dir = tempdir().unwrap();
    let config_path = dir.path().join("config.toml");

    let config_content = r#"
[server]
protocol = "http"
host = "127.0.0.1"
port = 3030
environment = "development"
log_level = "info"

[database]
url = "postgresql://localhost:5432/test"
max_connections = 10
min_connections = 1
connect_timeout = 30
idle_timeout = 600
max_lifetime = 1800

[session]
secret = "test-secret"
cookie_name = "session_id"
cookie_secure = false
cookie_http_only = true
cookie_same_site = "lax"
max_age = 3600

[cors]
allowed_origins = ["http://localhost:3030"]
allowed_methods = ["GET", "POST"]
allowed_headers = ["Content-Type"]
allow_credentials = true
max_age = 3600

[static]
assets_dir = "public"
site_root = "target/site"
site_pkg_dir = "pkg"

[server_dirs]
public_dir = "public"
uploads_dir = "uploads"
logs_dir = "logs"
temp_dir = "tmp"
cache_dir = "cache"
config_dir = "config"
data_dir = "data"
backup_dir = "backups"

[security]
enable_csrf = true
csrf_token_name = "csrf_token"
rate_limit_requests = 100
rate_limit_window = 60
bcrypt_cost = 12

[oauth]
enabled = false

[email]
enabled = false
provider = "console"
smtp_host = "localhost"
smtp_port = 587
smtp_username = ""
smtp_password = ""
smtp_use_tls = true
smtp_use_starttls = false
sendgrid_api_key = ""
sendgrid_endpoint = "https://api.sendgrid.com/v3/mail/send"
from_email = "test@example.com"
from_name = "Test"
template_dir = "templates/email"

[redis]
enabled = false
url = "redis://localhost:6379"
pool_size = 10
connection_timeout = 5
command_timeout = 5

[app]
name = "Test App"
version = "0.1.0"
debug = true
enable_metrics = false
enable_health_check = true
enable_compression = true
max_request_size = 10485760

[logging]
format = "text"
level = "info"
file_path = "logs/app.log"
max_file_size = 10485760
max_files = 5
enable_console = true
enable_file = false

[content]
enabled = false
content_dir = "content"
cache_enabled = true
cache_ttl = 3600
max_file_size = 5242880

[features]
[features.auth]
enabled = true
jwt = true
oauth = false
two_factor = false
sessions = true
password_reset = true
email_verification = true
account_lockout = true

[features.rbac]
enabled = false
database_access = false
file_access = false
content_access = false
api_access = false
categories = false
tags = false
caching = false
audit_logging = false
toml_config = false
hierarchical_permissions = false
dynamic_rules = false

[features.content]
enabled = true
markdown = true
syntax_highlighting = false
file_uploads = false
versioning = false
scheduling = false
seo = false

[features.security]
csrf = true
security_headers = true
rate_limiting = true
input_sanitization = true
sql_injection_protection = true
xss_protection = true
content_security_policy = true

[features.performance]
response_caching = false
query_caching = false
compression = true
connection_pooling = true
lazy_loading = false
background_tasks = false

[features.custom]
"#;

    fs::write(&config_path, config_content).unwrap();

    let config = Config::load_from_file(&config_path).unwrap();

    // Verify server configuration
    assert_eq!(config.server.host, "127.0.0.1");
    assert_eq!(config.server.port, 3030);
    assert_eq!(config.server.log_level, "info");
    assert!(matches!(config.server.protocol, Protocol::Http));
    assert!(matches!(
        config.server.environment,
        Environment::Development
    ));

    // Verify database configuration
    assert_eq!(config.database.url, "postgresql://localhost:5432/test");
    assert_eq!(config.database.max_connections, 10);

    // Verify application configuration
    assert_eq!(config.app.name, "Test App");
    assert_eq!(config.app.version, "0.1.0");
    assert_eq!(config.app.debug, true);

    // Verify server directories configuration
    assert_eq!(config.server_dirs.public_dir, "public");
    assert_eq!(config.server_dirs.uploads_dir, "uploads");
    assert_eq!(config.server_dirs.logs_dir, "logs");
    assert_eq!(config.server_dirs.cache_dir, "cache");

    // Verify helper methods
    assert_eq!(config.server_address(), "127.0.0.1:3030");
    assert_eq!(config.server_url(), "http://127.0.0.1:3030");
    assert_eq!(config.is_development(), true);
    assert_eq!(config.is_production(), false);
    assert_eq!(config.requires_tls(), false);
}

#[tokio::test]
async fn test_https_configuration_validation() {
    let dir = tempdir().unwrap();
    let config_path = dir.path().join("config.toml");

    // Create a configuration that should fail validation (HTTPS without TLS config)
    let config_content = r#"
[server]
protocol = "https"
host = "127.0.0.1"
port = 443
environment = "production"
log_level = "info"

[database]
url = "postgresql://localhost:5432/test"
max_connections = 10
min_connections = 1
connect_timeout = 30
idle_timeout = 600
max_lifetime = 1800

[session]
secret = "test-secret"
cookie_name = "session_id"
cookie_secure = false
cookie_http_only = true
cookie_same_site = "lax"
max_age = 3600

[cors]
allowed_origins = ["http://localhost:3030"]
allowed_methods = ["GET", "POST"]
allowed_headers = ["Content-Type"]
allow_credentials = true
max_age = 3600

[static]
assets_dir = "public"
site_root = "target/site"
site_pkg_dir = "pkg"

[server_dirs]
public_dir = "public"
uploads_dir = "uploads"
logs_dir = "logs"
temp_dir = "tmp"
cache_dir = "cache"
config_dir = "config"
data_dir = "data"
backup_dir = "backups"

[security]
enable_csrf = true
csrf_token_name = "csrf_token"
rate_limit_requests = 100
rate_limit_window = 60
bcrypt_cost = 12

[oauth]
enabled = false

[email]
enabled = false
provider = "console"
smtp_host = "localhost"
smtp_port = 587
smtp_username = ""
smtp_password = ""
smtp_use_tls = true
smtp_use_starttls = false
sendgrid_api_key = ""
sendgrid_endpoint = "https://api.sendgrid.com/v3/mail/send"
from_email = "test@example.com"
from_name = "Test"
template_dir = "templates/email"

[redis]
enabled = false
url = "redis://localhost:6379"
pool_size = 10
connection_timeout = 5
command_timeout = 5

[app]
name = "Test App"
version = "0.1.0"
debug = true
enable_metrics = false
enable_health_check = true
enable_compression = true
max_request_size = 10485760

[logging]
format = "text"
level = "info"
file_path = "logs/app.log"
max_file_size = 10485760
max_files = 5
enable_console = true
enable_file = false

[content]
enabled = false
content_dir = "content"
cache_enabled = true
cache_ttl = 3600
max_file_size = 5242880

[features]
[features.auth]
enabled = true
jwt = true
oauth = false
two_factor = false
sessions = true
password_reset = true
email_verification = true

[features.rbac]
enabled = false
database_access = false
file_access = false
content_access = false
api_access = false
categories = false
tags = false
caching = false
audit_logging = false
toml_config = false
hierarchical_permissions = false
dynamic_rules = false

[features.content]
enabled = true
markdown = true
syntax_highlighting = false
file_uploads = false
versioning = false
scheduling = false
seo = false

[features.security]
csrf = true
security_headers = true
rate_limiting = true
input_sanitization = true
sql_injection_protection = true
xss_protection = true
content_security_policy = true

[features.performance]
response_caching = false
query_caching = false
compression = true
connection_pooling = true
lazy_loading = false
background_tasks = false

[features.custom]
"#;

    fs::write(&config_path, config_content).unwrap();

    let config = Config::load_from_file(&config_path).unwrap();

    // This should fail because HTTPS is enabled but no TLS config is provided
    let result = config.validate();
    assert!(result.is_err());

    match result.unwrap_err() {
        ConfigError::ValidationError(msg) => {
            assert!(msg.contains("HTTPS protocol requires TLS configuration"));
        }
        other => {
            panic!("Expected ValidationError about HTTPS/TLS, got: {:?}", other);
        }
    }
}

#[tokio::test]
async fn test_invalid_toml_format() {
    let dir = tempdir().unwrap();
    let config_path = dir.path().join("config.toml");

    // Create an invalid TOML file
    let invalid_toml = r#"
[server
protocol = "http"
host = "127.0.0.1"
port = 3030
"#;

    fs::write(&config_path, invalid_toml).unwrap();

    let result = Config::load_from_file(&config_path);
    assert!(result.is_err());

    match result.unwrap_err() {
        ConfigError::ParseError(_) => {
            // Expected error
        }
        other => {
            panic!("Expected ParseError, got: {:?}", other);
        }
    }
}

#[tokio::test]
async fn test_database_pool_config() {
    let dir = tempdir().unwrap();
    let config_path = dir.path().join("config.toml");

    let config_content = r#"
[server]
protocol = "http"
host = "127.0.0.1"
port = 3030
environment = "development"
log_level = "info"

[database]
url = "postgresql://localhost:5432/test"
max_connections = 15
min_connections = 2
connect_timeout = 45
idle_timeout = 900
max_lifetime = 3600

[session]
secret = "test-secret"
cookie_name = "session_id"
cookie_secure = false
cookie_http_only = true
cookie_same_site = "lax"
max_age = 3600

[cors]
allowed_origins = ["http://localhost:3030"]
allowed_methods = ["GET", "POST"]
allowed_headers = ["Content-Type"]
allow_credentials = true
max_age = 3600

[static]
assets_dir = "public"
site_root = "target/site"
site_pkg_dir = "pkg"

[server_dirs]
public_dir = "public"
uploads_dir = "uploads"
logs_dir = "logs"
temp_dir = "tmp"
cache_dir = "cache"
config_dir = "config"
data_dir = "data"
backup_dir = "backups"

[security]
enable_csrf = true
csrf_token_name = "csrf_token"
rate_limit_requests = 100
rate_limit_window = 60
bcrypt_cost = 12

[oauth]
enabled = false

[email]
enabled = false
provider = "console"
smtp_host = "localhost"
smtp_port = 587
smtp_username = ""
smtp_password = ""
smtp_use_tls = true
smtp_use_starttls = false
sendgrid_api_key = ""
sendgrid_endpoint = "https://api.sendgrid.com/v3/mail/send"
from_email = "test@example.com"
from_name = "Test"
template_dir = "templates/email"

[redis]
enabled = false
url = "redis://localhost:6379"
pool_size = 10
connection_timeout = 5
command_timeout = 5

[app]
name = "Test App"
version = "0.1.0"
debug = true
enable_metrics = false
enable_health_check = true
enable_compression = true
max_request_size = 10485760

[logging]
format = "text"
level = "info"
file_path = "logs/app.log"
max_file_size = 10485760
max_files = 5
enable_console = true
enable_file = false

[content]
enabled = false
content_dir = "content"
cache_enabled = true
cache_ttl = 3600
max_file_size = 5242880

[features]
[features.auth]
enabled = true
jwt = true
oauth = false
two_factor = false
sessions = true
password_reset = true
email_verification = true

[features.rbac]
enabled = false
database_access = false
file_access = false
content_access = false
api_access = false
categories = false
tags = false
caching = false
audit_logging = false
toml_config = false
hierarchical_permissions = false
dynamic_rules = false

[features.content]
enabled = true
markdown = true
syntax_highlighting = false
file_uploads = false
versioning = false
scheduling = false
seo = false

[features.security]
csrf = true
security_headers = true
rate_limiting = true
input_sanitization = true
sql_injection_protection = true
xss_protection = true
content_security_policy = true

[features.performance]
response_caching = false
query_caching = false
compression = true
connection_pooling = true
lazy_loading = false
background_tasks = false

[features.custom]
"#;

    fs::write(&config_path, config_content).unwrap();

    let config = Config::load_from_file(&config_path).unwrap();
    let pool_config = config.database_pool_config();

    assert_eq!(pool_config.url, "postgresql://localhost:5432/test");
    assert_eq!(pool_config.max_connections, 15);
    assert_eq!(pool_config.min_connections, 2);
    assert_eq!(
        pool_config.connect_timeout,
        std::time::Duration::from_secs(45)
    );
    assert_eq!(
        pool_config.idle_timeout,
        std::time::Duration::from_secs(900)
    );
    assert_eq!(
        pool_config.max_lifetime,
        std::time::Duration::from_secs(3600)
    );
}

#[test]
fn test_environment_string_substitution_mock() {
    // Test the string substitution function with a mock approach
    // This test assumes DB_PASSWORD is already set in environment
    let input_without_substitution = "postgresql://user:password@localhost:5432/db";

    // Test that strings without substitution pass through unchanged
    let result = Config::substitute_env_in_string(input_without_substitution);
    assert_eq!(result, "postgresql://user:password@localhost:5432/db");
}

#[test]
fn test_config_defaults() {
    let config = Config::default();

    // Test default values
    assert_eq!(config.server.host, "127.0.0.1");
    assert_eq!(config.server.port, 3030);
    assert!(matches!(config.server.protocol, Protocol::Http));
    assert!(matches!(
        config.server.environment,
        Environment::Development
    ));
    assert_eq!(config.app.name, "My Rust App");
    assert_eq!(config.app.debug, true);
    assert_eq!(config.features.auth.enabled, true);
    assert_eq!(config.features.security.csrf, true);

    // Test server directories defaults
    assert_eq!(config.server_dirs.public_dir, "public");
    assert_eq!(config.server_dirs.uploads_dir, "uploads");
    assert_eq!(config.server_dirs.logs_dir, "logs");
    assert_eq!(config.server_dirs.temp_dir, "tmp");
    assert_eq!(config.server_dirs.cache_dir, "cache");
    assert_eq!(config.server_dirs.config_dir, "config");
    assert_eq!(config.server_dirs.data_dir, "data");
    assert_eq!(config.server_dirs.backup_dir, "backups");
}

#[test]
fn test_config_helper_methods() {
    let mut config = Config::default();

    // Test HTTP configuration
    assert_eq!(config.server_address(), "127.0.0.1:3030");
    assert_eq!(config.server_url(), "http://127.0.0.1:3030");
    assert_eq!(config.is_development(), true);
    assert_eq!(config.is_production(), false);
    assert_eq!(config.requires_tls(), false);

    // Test HTTPS configuration
    config.server.protocol = Protocol::Https;
    config.server.port = 443;
    config.server.environment = Environment::Production;

    assert_eq!(config.server_address(), "127.0.0.1:443");
    assert_eq!(config.server_url(), "https://127.0.0.1:443");
    assert_eq!(config.is_development(), false);
    assert_eq!(config.is_production(), true);
    assert_eq!(config.requires_tls(), true);
}
