//! Python-specific error handling and conversion

use crate::errors::FalcornError;
use pyo3::exceptions::{PyImportError, PyRuntimeError, PyTypeError, PyValueError};
use pyo3::prelude::*;

/// Convert PyErr to FalcornError
pub fn convert_py_error(err: PyErr) -> FalcornError {
    Python::attach(|py| {
        let err_str = err.to_string();

        // Check error type
        if err.is_instance_of::<PyImportError>(py) {
            FalcornError::PythonImportError { reason: err_str }
        } else if err.is_instance_of::<PyValueError>(py) {
            FalcornError::PythonTypeError { reason: err_str }
        } else if err.is_instance_of::<PyTypeError>(py) {
            FalcornError::PythonTypeError { reason: err_str }
        } else {
            FalcornError::PythonError { message: err_str }
        }
    })
}

/// Convert FalcornError to PyErr
pub fn convert_to_py_error(err: FalcornError) -> PyErr {
    match err {
        FalcornError::PythonError { message } => PyRuntimeError::new_err(message),
        FalcornError::PythonImportError { reason } => PyImportError::new_err(reason),
        FalcornError::PythonTypeError { reason } => PyTypeError::new_err(reason),
        FalcornError::PythonAppNotFound { module, callable } => {
            PyImportError::new_err(format!("Application not found: {}:{}", module, callable))
        }
        other => PyRuntimeError::new_err(other.to_string()),
    }
}

/// Python exception context for better error messages
pub struct PythonExceptionContext {
    pub traceback: Option<String>,
    pub exception_type: String,
    pub message: String,
}

impl PythonExceptionContext {
    /// Extract context from PyErr
    pub fn from_py_err(err: &PyErr) -> Self {
        Python::attach(|py| {
            let traceback = err.traceback(py).map(|tb| {
                tb.format()
                    .unwrap_or_else(|_| "No traceback available".to_string())
            });

            let exception_type = err.get_type(py).name().unwrap().to_string();
            let message = err.to_string();

            Self {
                traceback,
                exception_type,
                message,
            }
        })
    }

    /// Format as detailed error string
    pub fn format_detailed(&self) -> String {
        let mut output = format!("{}: {}", self.exception_type, self.message);

        if let Some(ref tb) = self.traceback {
            output.push_str("\n\nTraceback:\n");
            output.push_str(tb);
        }

        output
    }
}

/// Helper to capture and log Python exceptions
pub fn log_python_exception(err: &PyErr) {
    let context = PythonExceptionContext::from_py_err(err);
    tracing::error!("Python exception occurred:\n{}", context.format_detailed());
}

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

    #[test]
    fn test_error_conversion() {
        let rust_err = FalcornError::PythonImportError {
            reason: "test error".to_string(),
        };
        let py_err = convert_to_py_error(rust_err);
        assert!(py_err.to_string().contains("test error"));
    }
}
