use deltalake::arrow::error::ArrowError;
use deltalake::datafusion::error::DataFusionError;
use deltalake::errors::DeltaTableError;
use deltalake::logstore::object_store::Error as ObjectStoreError;
use std::os::raw::c_char;

use crate::utils::string_to_c_str;

// Helper functions to create error wrappers
impl DeltaGoError {
    pub fn invalid_uri(msg: String) -> Self {
        DeltaGoError::InvalidUri(StringWrapper {
            message: string_to_c_str(msg),
        })
    }

    pub fn invalid_schema(msg: String) -> Self {
        DeltaGoError::InvalidSchema(StringWrapper {
            message: string_to_c_str(msg),
        })
    }

    pub fn invalid_configuration(msg: String) -> Self {
        DeltaGoError::InvalidConfiguration(StringWrapper {
            message: string_to_c_str(msg),
        })
    }

    pub fn invalid_storage_options(msg: String) -> Self {
        DeltaGoError::InvalidStorageOptions(StringWrapper {
            message: string_to_c_str(msg),
        })
    }

    pub fn delta_table_error(msg: String) -> Self {
        DeltaGoError::DeltaTable(DeltaTableErrorWrapper {
            message: string_to_c_str(msg),
        })
    }

    pub fn null_pointer(_msg: String) -> Self {
        DeltaGoError::NullPointer
    }
}

#[repr(C)]
pub struct CResult<T> {
    pub success: bool,
    pub value: T,
    pub error: *mut DeltaGoError,
}

impl<T> CResult<T>
where
    T: Default,
{
    pub fn new() -> Self {
        Self {
            success: false,
            value: T::default(),
            error: std::ptr::null_mut(),
        }
    }
}

#[derive(Debug)]
#[repr(C)]
pub enum DeltaGoError {
    DeltaTable(DeltaTableErrorWrapper),
    ObjectStore(ObjectStoreErrorWrapper),
    Arrow(ArrowErrorWrapper),
    DataFusion(DataFusionErrorWrapper),
    InvalidUri(StringWrapper),
    InvalidSchema(StringWrapper),
    InvalidConfiguration(StringWrapper),
    InvalidStorageOptions(StringWrapper),
    NullPointer,
    InvalidUtf8,
    IoError(StringWrapper),
    SerializationError(StringWrapper),
}

#[repr(C)]
#[derive(Debug)]
pub struct DeltaTableErrorWrapper {
    pub message: *const c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct ObjectStoreErrorWrapper {
    pub message: *const c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct ArrowErrorWrapper {
    pub message: *const c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct DataFusionErrorWrapper {
    pub message: *const c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct StringWrapper {
    pub message: *const c_char,
}

impl From<DeltaTableError> for DeltaGoError {
    fn from(err: DeltaTableError) -> Self {
        DeltaGoError::DeltaTable(DeltaTableErrorWrapper {
            message: string_to_c_str(err.to_string()),
        })
    }
}

impl From<ObjectStoreError> for DeltaGoError {
    fn from(err: ObjectStoreError) -> Self {
        DeltaGoError::ObjectStore(ObjectStoreErrorWrapper {
            message: string_to_c_str(err.to_string()),
        })
    }
}

impl From<ArrowError> for DeltaGoError {
    fn from(err: ArrowError) -> Self {
        DeltaGoError::Arrow(ArrowErrorWrapper {
            message: string_to_c_str(err.to_string()),
        })
    }
}

impl From<DataFusionError> for DeltaGoError {
    fn from(err: DataFusionError) -> Self {
        DeltaGoError::DataFusion(DataFusionErrorWrapper {
            message: string_to_c_str(err.to_string()),
        })
    }
}

impl From<std::ffi::NulError> for DeltaGoError {
    fn from(_: std::ffi::NulError) -> Self {
        DeltaGoError::NullPointer
    }
}

impl From<std::str::Utf8Error> for DeltaGoError {
    fn from(_: std::str::Utf8Error) -> Self {
        DeltaGoError::InvalidUtf8
    }
}

impl From<std::io::Error> for DeltaGoError {
    fn from(err: std::io::Error) -> Self {
        DeltaGoError::IoError(StringWrapper {
            message: string_to_c_str(err.to_string()),
        })
    }
}

impl From<serde_json::Error> for DeltaGoError {
    fn from(err: serde_json::Error) -> Self {
        DeltaGoError::SerializationError(StringWrapper {
            message: string_to_c_str(err.to_string()),
        })
    }
}

/// Get error message as C string
#[no_mangle]
pub extern "C" fn deltalake_error_message(error: *const DeltaGoError) -> *const c_char {
    if error.is_null() {
        return std::ptr::null();
    }

    unsafe {
        match &*error {
            DeltaGoError::DeltaTable(err) => err.message,
            DeltaGoError::ObjectStore(err) => err.message,
            DeltaGoError::Arrow(err) => err.message,
            DeltaGoError::DataFusion(err) => err.message,
            DeltaGoError::InvalidUri(err) => err.message,
            DeltaGoError::InvalidSchema(err) => err.message,
            DeltaGoError::InvalidConfiguration(err) => err.message,
            DeltaGoError::InvalidStorageOptions(err) => err.message,
            DeltaGoError::IoError(err) => err.message,
            DeltaGoError::SerializationError(err) => err.message,
            DeltaGoError::NullPointer => string_to_c_str("Null pointer error".to_string()),
            DeltaGoError::InvalidUtf8 => string_to_c_str("Invalid UTF-8 string".to_string()),
        }
    }
}

/// Get error type as integer
#[no_mangle]
pub extern "C" fn deltalake_error_type(error: *const DeltaGoError) -> i32 {
    if error.is_null() {
        return -1;
    }

    unsafe {
        match &*error {
            DeltaGoError::DeltaTable(_) => 0,
            DeltaGoError::ObjectStore(_) => 1,
            DeltaGoError::Arrow(_) => 2,
            DeltaGoError::DataFusion(_) => 3,
            DeltaGoError::InvalidUri(_) => 4,
            DeltaGoError::InvalidSchema(_) => 5,
            DeltaGoError::InvalidConfiguration(_) => 6,
            DeltaGoError::InvalidStorageOptions(_) => 7,
            DeltaGoError::NullPointer => 8,
            DeltaGoError::InvalidUtf8 => 9,
            DeltaGoError::IoError(_) => 10,
            DeltaGoError::SerializationError(_) => 11,
        }
    }
}
