// This file is dual licensed under the terms of the Apache License, Version
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use asn1::Asn1Writable;
use pyo3::types::PyAnyMethods;

use crate::declarative_asn1::types as asn1_types;

#[pyo3::pyfunction]
pub(crate) fn encode_der<'p>(
    py: pyo3::Python<'p>,
    value: &pyo3::Bound<'p, pyo3::types::PyAny>,
) -> pyo3::PyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
    let class = value.get_type();

    // TODO error messages are lost since asn1::WriteError does not allow
    // specifying error messages
    let encoded_bytes = if let Ok(root) = class.getattr("__asn1_root__") {
        let root = root.downcast::<asn1_types::AnnotatedType>().map_err(|_| {
            pyo3::exceptions::PyValueError::new_err(
                "target type has invalid annotations".to_string(),
            )
        })?;
        let object = asn1_types::AnnotatedTypeObject {
            annotated_type: root.get(),
            value: value.clone(),
        };
        asn1::write(|writer| object.write(writer))
            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?
    } else {
        // Handle simple types directly
        let annotated_type = asn1_types::non_root_type_to_annotated(py, &class, None)?;
        let object = asn1_types::AnnotatedTypeObject {
            annotated_type: &annotated_type,
            value: value.clone(),
        };
        asn1::write(|writer| object.write(writer))
            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?
    };

    Ok(pyo3::types::PyBytes::new(py, &encoded_bytes))
}
