use crate::errors::{ConnectionError, ConnectionTestResult, PleskError};
use crate::models::PleskConfig;
use serde::{Deserialize, Serialize};
use std::time::Instant;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PleskServer {
    pub id: i32,
    pub name: String,
    pub hostname: String,
    pub status: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PleskCustomer {
    pub id: i32,
    pub username: String,
    pub firstname: String,
    pub lastname: String,
    pub email: String,
    pub phone: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PleskSubscription {
    pub id: i32,
    pub name: String,
    pub owner_id: i32,
    pub plan_id: i32,
    pub status: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PleskDomain {
    pub id: i32,
    pub name: String,
    pub subscription_id: i32,
    pub status: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PleskUsageMetrics {
    pub subscription_id: i32,
    pub cpu_usage: f64,
    pub ram_usage: f64,
    pub disk_usage: f64,
    pub bandwidth_usage: f64,
    pub database_usage: f64,
}

/// Validate Plesk configuration
pub fn validate_plesk_config(config: &PleskConfig) -> Result<(), PleskError> {
    if config.host.is_empty() {
        return Err(PleskError::InvalidConfig {
            field: "host".to_string(),
            message: "Host is required".to_string(),
        });
    }

    if config.port == 0 {
        return Err(PleskError::InvalidConfig {
            field: "port".to_string(),
            message: "Port must be between 1 and 65535".to_string(),
        });
    }

    if config.api_key.is_empty() && config.username.is_empty() {
        return Err(PleskError::InvalidConfig {
            field: "credentials".to_string(),
            message: "Either API key or username must be provided".to_string(),
        });
    }

    if !config.username.is_empty() && config.password.is_empty() {
        return Err(PleskError::InvalidConfig {
            field: "password".to_string(),
            message: "Password is required when username is provided".to_string(),
        });
    }

    Ok(())
}

/// Test Plesk connection with comprehensive error handling
pub fn test_plesk_connection(
    config: &PleskConfig,
    _session_id: Option<&str>,
    _timeout_secs: Option<u64>,
) -> ConnectionTestResult {
    let start = Instant::now();

    // Validate configuration first
    if let Err(e) = validate_plesk_config(config) {
        return ConnectionTestResult {
            success: false,
            message: e.to_string(),
            latency_ms: Some(start.elapsed().as_millis() as u64),
            error: Some(ConnectionError::from(e)),
            requires_2fa: false,
            session_id: None,
            two_factor_method: None,
        };
    }

    // Build the Plesk API URL
    let protocol = if config.use_https { "https" } else { "http" };
    let url = format!(
        "{}://{}:{}/enterprise/control/agent.php",
        protocol, config.host, config.port
    );

    log::info!("Testing Plesk connection to: {}", url);

    // Build request
    let request = if !config.api_key.is_empty() {
        ureq::get(&url).header("X-API-Key", &config.api_key)
    } else if !config.username.is_empty() {
        let credentials = base64::Engine::encode(
            &base64::engine::general_purpose::STANDARD,
            format!("{}:{}", config.username, config.password),
        );
        ureq::get(&url).header("Authorization", &format!("Basic {}", credentials))
    } else {
        ureq::get(&url)
    };

    // Execute request
    match request.call() {
        Ok(response) => {
            let latency = start.elapsed().as_millis() as u64;
            let status = response.status();

            if status == 200 {
                ConnectionTestResult {
                    success: true,
                    message: "Connected to Plesk successfully".to_string(),
                    latency_ms: Some(latency),
                    error: None,
                    requires_2fa: false,
                    session_id: None,
                    two_factor_method: None,
                }
            } else if status == 401 {
                ConnectionTestResult {
                    success: false,
                    message: "Authentication failed".to_string(),
                    latency_ms: Some(latency),
                    error: Some(ConnectionError::from(PleskError::AuthenticationFailed {
                        reason: "Invalid credentials".to_string(),
                    })),
                    requires_2fa: false,
                    session_id: None,
                    two_factor_method: None,
                }
            } else {
                ConnectionTestResult {
                    success: false,
                    message: format!("Unexpected status code: {}", status),
                    latency_ms: Some(latency),
                    error: Some(ConnectionError::from(PleskError::ApiError {
                        code: status.as_u16() as i32,
                        message: format!("HTTP {}", status),
                    })),
                    requires_2fa: false,
                    session_id: None,
                    two_factor_method: None,
                }
            }
        }
        Err(e) => {
            let latency = start.elapsed().as_millis() as u64;
            let reason = e.to_string();

            let error = if reason.contains("timed out") || reason.contains("timeout") {
                PleskError::Timeout {
                    duration_ms: latency,
                }
            } else if reason.contains("certificate")
                || reason.contains("SSL")
                || reason.contains("TLS")
            {
                PleskError::SslError {
                    reason: reason.clone(),
                }
            } else {
                PleskError::ConnectionFailed {
                    host: config.host.clone(),
                    reason: reason.clone(),
                }
            };

            ConnectionTestResult {
                success: false,
                message: format!("Connection failed: {}", reason),
                latency_ms: Some(latency),
                error: Some(ConnectionError::from(error)),
                requires_2fa: false,
                session_id: None,
                two_factor_method: None,
            }
        }
    }
}

/// Legacy function for backward compatibility
pub fn test_plesk_connection_impl(
    config: &PleskConfig,
    session_id: Option<&str>,
) -> Result<crate::models::PleskTestResult, String> {
    let result = test_plesk_connection(config, session_id, None);

    if result.requires_2fa {
        return Ok(crate::models::PleskTestResult::Requires2FA {
            session_id: result.session_id.unwrap_or_default(),
            method: result
                .two_factor_method
                .unwrap_or_else(|| "totp".to_string()),
        });
    }

    if result.success {
        Ok(crate::models::PleskTestResult::Success {
            message: result.message,
        })
    } else {
        Ok(crate::models::PleskTestResult::Error {
            message: result.error.map(|e| e.message).unwrap_or(result.message),
        })
    }
}

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

    fn create_test_config() -> PleskConfig {
        PleskConfig {
            host: "plesk.example.com".to_string(),
            port: 8443,
            username: "admin".to_string(),
            password: "password123".to_string(),
            api_key: String::new(),
            use_https: true,
            verify_ssl: true,
            two_factor_enabled: false,
            two_factor_method: "none".to_string(),
            two_factor_secret: None,
            session_id: None,
        }
    }

    #[test]
    fn test_validate_config_empty_host() {
        let mut config = create_test_config();
        config.host = String::new();

        let result = validate_plesk_config(&config);
        assert!(result.is_err());

        if let Err(PleskError::InvalidConfig { field, .. }) = result {
            assert_eq!(field, "host");
        } else {
            panic!("Expected InvalidConfig error");
        }
    }

    #[test]
    fn test_validate_config_invalid_port() {
        let mut config = create_test_config();
        config.port = 0;

        let result = validate_plesk_config(&config);
        assert!(result.is_err());

        if let Err(PleskError::InvalidConfig { field, .. }) = result {
            assert_eq!(field, "port");
        } else {
            panic!("Expected InvalidConfig error");
        }
    }

    #[test]
    fn test_validate_config_no_credentials() {
        let mut config = create_test_config();
        config.api_key = String::new();
        config.username = String::new();

        let result = validate_plesk_config(&config);
        assert!(result.is_err());

        if let Err(PleskError::InvalidConfig { field, .. }) = result {
            assert_eq!(field, "credentials");
        } else {
            panic!("Expected InvalidConfig error");
        }
    }

    #[test]
    fn test_validate_config_username_without_password() {
        let mut config = create_test_config();
        config.api_key = String::new();
        config.password = String::new();

        let result = validate_plesk_config(&config);
        assert!(result.is_err());

        if let Err(PleskError::InvalidConfig { field, .. }) = result {
            assert_eq!(field, "password");
        } else {
            panic!("Expected InvalidConfig error");
        }
    }

    #[test]
    fn test_validate_config_valid_with_api_key() {
        let mut config = create_test_config();
        config.api_key = "test-api-key".to_string();
        config.username = String::new();
        config.password = String::new();

        let result = validate_plesk_config(&config);
        assert!(result.is_ok());
    }

    #[test]
    fn test_validate_config_valid_with_credentials() {
        let config = create_test_config();
        let result = validate_plesk_config(&config);
        assert!(result.is_ok());
    }

    #[test]
    fn test_connection_test_invalid_host() {
        let mut config = create_test_config();
        config.host = String::new();

        let result = test_plesk_connection(&config, None, Some(5));
        assert!(!result.success);
        assert!(result.error.is_some());
        assert!(result.latency_ms.is_some());
    }

    #[test]
    fn test_connection_test_unreachable_host() {
        let mut config = create_test_config();
        config.host = "192.0.2.1".to_string(); // TEST-NET, should timeout
        config.port = 1;

        let result = test_plesk_connection(&config, None, Some(2));
        assert!(!result.success);
        assert!(result.error.is_some());
        assert!(result.latency_ms.is_some());
    }

    #[test]
    fn test_plesk_error_to_connection_error() {
        let error = PleskError::ConnectionFailed {
            host: "example.com".to_string(),
            reason: "Connection refused".to_string(),
        };

        let conn_error: ConnectionError = error.into();
        assert_eq!(conn_error.error_type, "connection");
        assert_eq!(conn_error.error_code, "PLESK_CONN_001");
    }

    #[test]
    fn test_plesk_timeout_error() {
        let error = PleskError::Timeout { duration_ms: 5000 };
        let conn_error: ConnectionError = error.into();
        assert_eq!(conn_error.error_type, "timeout");
        assert_eq!(conn_error.error_code, "PLESK_TIMEOUT_001");
    }

    #[test]
    fn test_plesk_ssl_error() {
        let error = PleskError::SslError {
            reason: "Certificate verification failed".to_string(),
        };
        let conn_error: ConnectionError = error.into();
        assert_eq!(conn_error.error_type, "ssl");
        assert_eq!(conn_error.error_code, "PLESK_SSL_001");
    }
}
