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

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SapSession {
    pub session_id: String,
    pub expiration: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SapCustomer {
    pub code: String,
    pub name: String,
    pub email: String,
    pub address: String,
    pub city: String,
    pub country: String,
    pub phone: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SapSubscription {
    pub id: String,
    pub name: String,
    pub start_date: String,
    pub end_date: String,
    pub status: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SapItem {
    pub code: String,
    pub name: String,
    pub description: String,
    pub price: f64,
}

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

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

    if config.company_db.is_empty() {
        return Err(SapError::InvalidConfig {
            field: "company_db".to_string(),
            message: "Company database is required".to_string(),
        });
    }

    if config.username.is_empty() {
        return Err(SapError::InvalidConfig {
            field: "username".to_string(),
            message: "Username is required".to_string(),
        });
    }

    if config.password.is_empty() {
        return Err(SapError::InvalidConfig {
            field: "password".to_string(),
            message: "Password is required".to_string(),
        });
    }

    Ok(())
}

/// Test SAP B1 Service Layer connection with comprehensive error handling
pub fn test_sap_connection(config: &SapConfig, _timeout_secs: Option<u64>) -> ConnectionTestResult {
    let start = Instant::now();

    // Validate configuration first
    if let Err(e) = validate_sap_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 SAP B1 Service Layer URL
    let protocol = if config.use_ssl { "https" } else { "http" };
    let url = format!(
        "{}://{}:{}/b1s/v1/Login",
        protocol, config.host, config.port
    );

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

    // Build login request body
    let login_body = serde_json::json!({
        "CompanyDB": config.company_db,
        "UserName": config.username,
        "Password": config.password,
    });

    // Execute request
    let request = ureq::post(&url)
        .header("Content-Type", "application/json")
        .header("Accept", "application/json");

    let body_str = login_body.to_string();
    let body_reader = body_str.as_bytes();

    match request.send(body_reader) {
        Ok(response) => {
            let latency = start.elapsed().as_millis() as u64;
            let status = response.status();

            if status == 200 {
                ConnectionTestResult {
                    success: true,
                    message: "Connected to SAP B1 successfully".to_string(),
                    latency_ms: Some(latency),
                    error: None,
                    requires_2fa: false,
                    session_id: None,
                    two_factor_method: None,
                }
            } else {
                // Parse SAP error response
                let body = response.into_body().read_to_string().unwrap_or_default();
                let error_message = serde_json::from_str::<serde_json::Value>(&body)
                    .ok()
                    .and_then(|v| {
                        v.get("error")
                            .and_then(|e| e.get("message"))
                            .and_then(|m| m.get("value"))
                            .and_then(|v| v.as_str())
                            .map(|s| s.to_string())
                    })
                    .unwrap_or(body);

                if status == 401 {
                    ConnectionTestResult {
                        success: false,
                        message: "Authentication failed".to_string(),
                        latency_ms: Some(latency),
                        error: Some(ConnectionError::from(SapError::AuthenticationFailed {
                            reason: error_message,
                        })),
                        requires_2fa: false,
                        session_id: None,
                        two_factor_method: None,
                    }
                } else {
                    ConnectionTestResult {
                        success: false,
                        message: format!("SAP login failed: {}", error_message),
                        latency_ms: Some(latency),
                        error: Some(ConnectionError::from(SapError::ApiError {
                            code: status.as_u16() as i32,
                            message: error_message,
                        })),
                        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") {
                SapError::Timeout {
                    duration_ms: latency,
                }
            } else if reason.contains("certificate")
                || reason.contains("SSL")
                || reason.contains("TLS")
            {
                SapError::SslError {
                    reason: reason.clone(),
                }
            } else {
                SapError::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 test result for backward compatibility
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SapConnectionTestResult {
    pub success: bool,
    pub message: String,
    pub session_id: Option<String>,
}

/// Legacy function for backward compatibility
pub fn test_sap_connection_impl(config: &SapConfig) -> SapConnectionTestResult {
    let result = test_sap_connection(config, None);

    SapConnectionTestResult {
        success: result.success,
        message: result.message,
        session_id: result.session_id,
    }
}

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

    fn create_test_config() -> SapConfig {
        SapConfig {
            host: "sap.example.com".to_string(),
            port: 50000,
            company_db: "SBODEMO".to_string(),
            username: "manager".to_string(),
            password: "password123".to_string(),
            use_ssl: true,
            timeout_seconds: 30,
        }
    }

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

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

        if let Err(SapError::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_sap_config(&config);
        assert!(result.is_err());

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

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

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

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

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

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

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

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

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

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

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

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

        let result = test_sap_connection(&config, 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_sap_connection(&config, Some(2));
        assert!(!result.success);
        assert!(result.error.is_some());
        assert!(result.latency_ms.is_some());
    }

    #[test]
    fn test_sap_error_to_connection_error() {
        let error = SapError::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, "SAP_CONN_001");
    }

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

    #[test]
    fn test_sap_ssl_error() {
        let error = SapError::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, "SAP_SSL_001");
    }

    #[test]
    fn test_sap_session_expired_error() {
        let error = SapError::SessionExpired;
        let conn_error: ConnectionError = error.into();
        assert_eq!(conn_error.error_type, "session");
        assert_eq!(conn_error.error_code, "SAP_SESSION_001");
    }
}
