use std::collections::HashSet;
use std::path::PathBuf;

use ::apk_info::Apk as ApkRust;
use ::apk_info::models::{
    Activity as ApkActivity, ActivityAlias as ApkActivityAlias, Attribution as ApkAttribution,
    IntentFilter as ApkIntentFilter, Permission as ApkPermission, Provider as ApkProvider,
    Receiver as ApkReceiver, Service as ApkService,
};
use ::apk_info_zip::{
    CertificateInfo as ZipCertificateInfo, FileCompressionType as ZipFileCompressionType,
    Signature as ZipSignature,
};
use pyo3::conversion::IntoPyObject;
use pyo3::exceptions::{PyException, PyFileNotFoundError, PyTypeError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::PyString;
use pyo3::{Bound, PyAny, PyResult, create_exception, pyclass, pymethods};

create_exception!(m, APKError, PyException, "Got error while parsing apk");

#[pyclass(eq, frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct CertificateInfo {
    #[pyo3(get)]
    pub serial_number: String,

    #[pyo3(get)]
    pub subject: String,

    #[pyo3(get)]
    pub issuer: String,

    #[pyo3(get)]
    pub valid_from: String,

    #[pyo3(get)]
    pub valid_until: String,

    #[pyo3(get)]
    pub signature_type: String,

    #[pyo3(get)]
    pub md5_fingerprint: String,

    #[pyo3(get)]
    pub sha1_fingerprint: String,

    #[pyo3(get)]
    pub sha256_fingerprint: String,
}

impl From<ZipCertificateInfo> for CertificateInfo {
    fn from(certificate: ZipCertificateInfo) -> Self {
        Self {
            serial_number: certificate.serial_number,
            subject: certificate.subject,
            issuer: certificate.issuer,
            valid_from: certificate.valid_from,
            valid_until: certificate.valid_until,
            signature_type: certificate.signature_type,
            md5_fingerprint: certificate.md5_fingerprint,
            sha1_fingerprint: certificate.sha1_fingerprint,
            sha256_fingerprint: certificate.sha256_fingerprint,
        }
    }
}

#[pymethods]
impl CertificateInfo {
    fn __repr__(&self) -> String {
        format!(
            "CertificateInfo(serial_number='{}', subject='{}', issuer='{}' valid_from='{}', valid_until='{}', signature_type='{}', md5_fingerprint='{}', sha1_fingerprint='{}', sha256_fingerprint='{}')",
            self.serial_number,
            self.subject,
            self.issuer,
            self.valid_from,
            self.valid_until,
            self.signature_type,
            self.md5_fingerprint,
            self.sha1_fingerprint,
            self.sha256_fingerprint
        )
    }
}

#[pyclass(eq, frozen, module = "apk_info._apk_info")]
#[derive(PartialEq, Eq, Hash)]
enum Signature {
    V1 { certificates: Vec<CertificateInfo> },
    V2 { certificates: Vec<CertificateInfo> },
    V3 { certificates: Vec<CertificateInfo> },
    V31 { certificates: Vec<CertificateInfo> },
    StampBlockV1 { certificate: CertificateInfo },
    StampBlockV2 { certificate: CertificateInfo },
    ApkChannelBlock { value: String },
    PackerNextGenV2 { value: Vec<u8> },
    GooglePlayFrosting {},
    VasDollyV2 { value: String },
}

impl Signature {
    fn from<'py>(py: Python<'py>, signature: ZipSignature) -> Option<Bound<'py, Signature>> {
        match signature {
            ZipSignature::V1(v) => Signature::V1 {
                certificates: v.into_iter().map(CertificateInfo::from).collect(),
            }
            .into_pyobject(py)
            .ok(),
            ZipSignature::V2(v) => Signature::V2 {
                certificates: v.into_iter().map(CertificateInfo::from).collect(),
            }
            .into_pyobject(py)
            .ok(),
            ZipSignature::V3(v) => Signature::V3 {
                certificates: v.into_iter().map(CertificateInfo::from).collect(),
            }
            .into_pyobject(py)
            .ok(),
            ZipSignature::V31(v) => Signature::V31 {
                certificates: v.into_iter().map(CertificateInfo::from).collect(),
            }
            .into_pyobject(py)
            .ok(),
            ZipSignature::StampBlockV1(v) => Signature::StampBlockV1 {
                certificate: v.into(),
            }
            .into_pyobject(py)
            .ok(),
            ZipSignature::StampBlockV2(v) => Signature::StampBlockV2 {
                certificate: v.into(),
            }
            .into_pyobject(py)
            .ok(),
            ZipSignature::ApkChannelBlock(value) => {
                Signature::ApkChannelBlock { value }.into_pyobject(py).ok()
            }
            ZipSignature::PackerNextGenV2(value) => {
                Signature::PackerNextGenV2 { value }.into_pyobject(py).ok()
            }
            ZipSignature::GooglePlayFrosting => {
                Signature::GooglePlayFrosting {}.into_pyobject(py).ok()
            }
            ZipSignature::VasDollyV2(v) => {
                Signature::VasDollyV2 { value: v }.into_pyobject(py).ok()
            }
            _ => None,
        }
    }
}

#[pymethods]
impl Signature {
    fn __repr__(&self) -> String {
        match self {
            Signature::V1 { certificates } => {
                format!("Signature.V1(certificates={:?})", certificates)
            }
            Signature::V2 { certificates } => {
                format!("Signature.V2(certificates={:?})", certificates)
            }
            Signature::V3 { certificates } => {
                format!("Signature.V3(certificates={:?})", certificates)
            }
            Signature::V31 { certificates } => {
                format!("Signature.V31(certificates={:?})", certificates)
            }
            Signature::StampBlockV1 { certificate } => {
                format!("Signature.StampBlockV1(certificate={:?})", certificate)
            }
            Signature::StampBlockV2 { certificate } => {
                format!("Signature.StampBlockV2(certificate={:?})", certificate)
            }
            Signature::ApkChannelBlock { value } => {
                format!("Signature.ApkChannelBlock(value='{}')", value)
            }
            Signature::PackerNextGenV2 { value } => {
                let hex_string = value
                    .iter()
                    .map(|b| format!("{:02x}", b))
                    .collect::<Vec<_>>()
                    .join("");
                format!("Signature.PackerNextGenV2(channel='{}')", hex_string)
            }
            Signature::GooglePlayFrosting {} => "Signature.GooglePlayFrosting()".to_string(),
            Signature::VasDollyV2 { value } => {
                format!("Signature.VasDollyV2(value='{}')", value)
            }
        }
    }
}

// NOTE: currently pyo3 handle's python enum not very well
// Maybe upgrade in the future: https://github.com/PyO3/pyo3/issues/2887
#[pyclass(eq, eq_int, frozen, module = "apk_info._apk_info")]
#[derive(PartialEq)]
enum FileCompressionType {
    #[pyo3(name = "STORED")]
    Stored,
    #[pyo3(name = "DEFLATED")]
    Deflated,
    #[pyo3(name = "STORED_TAMPERED")]
    StoredTampered,
    #[pyo3(name = "DEFLATED_TAMPERED")]
    DeflatedTampered,
}

#[pymethods]
impl FileCompressionType {
    fn __repr__(&self) -> &'static str {
        match self {
            FileCompressionType::Stored => "stored",
            FileCompressionType::Deflated => "deflated",
            FileCompressionType::StoredTampered => "stored_tampered",
            FileCompressionType::DeflatedTampered => "deflated_tampered",
        }
    }
}

impl From<ZipFileCompressionType> for FileCompressionType {
    fn from(kind: ZipFileCompressionType) -> Self {
        match kind {
            ZipFileCompressionType::Stored => FileCompressionType::Stored,
            ZipFileCompressionType::Deflated => FileCompressionType::Deflated,
            ZipFileCompressionType::StoredTampered => FileCompressionType::StoredTampered,
            ZipFileCompressionType::DeflatedTampered => FileCompressionType::DeflatedTampered,
        }
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct IntentFilter {
    #[pyo3(get)]
    actions: Vec<String>,

    #[pyo3(get)]
    categories: Vec<String>,
}

impl<'a> From<ApkIntentFilter<'a>> for IntentFilter {
    fn from(intent: ApkIntentFilter<'a>) -> Self {
        IntentFilter {
            actions: intent.actions.into_iter().map(String::from).collect(),
            categories: intent.categories.into_iter().map(String::from).collect(),
        }
    }
}

#[pymethods]
impl IntentFilter {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            // Option<T>
            (opt $field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };

            // Vec<T> (skip if empty)
            (vec $field:ident) => {
                if !self.$field.is_empty() {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), self.$field));
                }
            };

            // Plain field (always print)
            ($field:ident) => {
                parts.push(format!(concat!(stringify!($field), "={:?}"), self.$field));
            };
        }

        push_field!(vec actions);
        push_field!(vec categories);

        format!("IntentFilter({})", parts.join(", "))
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct Activity {
    #[pyo3(get)]
    enabled: Option<String>,
    #[pyo3(get)]
    exported: Option<String>,
    #[pyo3(get)]
    icon: Option<String>,
    #[pyo3(get)]
    label: Option<String>,
    #[pyo3(get)]
    name: Option<String>,
    #[pyo3(get)]
    parent_activity_name: Option<String>,
    #[pyo3(get)]
    permission: Option<String>,
    #[pyo3(get)]
    process: Option<String>,
    #[pyo3(get)]
    intent_filters: Vec<IntentFilter>,
}

impl<'a> From<ApkActivity<'a>> for Activity {
    fn from(activity: ApkActivity<'a>) -> Self {
        Activity {
            enabled: activity.enabled.map(String::from),
            exported: activity.exported.map(String::from),
            icon: activity.icon.map(String::from),
            label: activity.label.map(String::from),
            name: activity.name.map(String::from),
            parent_activity_name: activity.parent_activity_name.map(String::from),
            permission: activity.permission.map(String::from),
            process: activity.process.map(String::from),
            intent_filters: activity
                .intent_filters
                .into_iter()
                .map(IntentFilter::from)
                .collect(),
        }
    }
}

#[pymethods]
impl Activity {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            // Option<T>
            (opt $field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };

            // Vec<T> (skip if empty)
            (vec $field:ident) => {
                if !self.$field.is_empty() {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), self.$field));
                }
            };

            // Plain field (always print)
            ($field:ident) => {
                parts.push(format!(concat!(stringify!($field), "={:?}"), self.$field));
            };
        }

        push_field!(opt enabled);
        push_field!(opt exported);
        push_field!(opt icon);
        push_field!(opt label);
        push_field!(opt name);
        push_field!(opt parent_activity_name);
        push_field!(opt permission);
        push_field!(opt process);
        push_field!(vec intent_filters);

        format!("Activity({})", parts.join(", "))
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct ActivityAlias {
    #[pyo3(get)]
    enabled: Option<String>,
    #[pyo3(get)]
    exported: Option<String>,
    #[pyo3(get)]
    icon: Option<String>,
    #[pyo3(get)]
    label: Option<String>,
    #[pyo3(get)]
    name: Option<String>,
    #[pyo3(get)]
    permission: Option<String>,
    #[pyo3(get)]
    target_activity: Option<String>,
    #[pyo3(get)]
    intent_filters: Vec<IntentFilter>,
}

impl<'a> From<ApkActivityAlias<'a>> for ActivityAlias {
    fn from(activity: ApkActivityAlias<'a>) -> Self {
        ActivityAlias {
            enabled: activity.enabled.map(String::from),
            exported: activity.exported.map(String::from),
            icon: activity.icon.map(String::from),
            label: activity.label.map(String::from),
            name: activity.name.map(String::from),
            permission: activity.permission.map(String::from),
            target_activity: activity.target_activity.map(String::from),
            intent_filters: activity
                .intent_filters
                .into_iter()
                .map(IntentFilter::from)
                .collect(),
        }
    }
}

#[pymethods]
impl ActivityAlias {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            // Option<T>
            (opt $field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };

            // Vec<T> (skip if empty)
            (vec $field:ident) => {
                if !self.$field.is_empty() {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), self.$field));
                }
            };

            // Plain field (always print)
            ($field:ident) => {
                parts.push(format!(concat!(stringify!($field), "={:?}"), self.$field));
            };
        }

        push_field!(opt enabled);
        push_field!(opt exported);
        push_field!(opt icon);
        push_field!(opt label);
        push_field!(opt name);
        push_field!(opt permission);
        push_field!(opt target_activity);
        push_field!(vec intent_filters);

        format!("ActivityAlias({})", parts.join(", "))
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct Permission {
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    icon: Option<String>,
    #[pyo3(get)]
    label: Option<String>,
    #[pyo3(get)]
    name: Option<String>,
    #[pyo3(get)]
    permission_group: Option<String>,
    #[pyo3(get)]
    protection_level: Option<String>,
}

impl<'a> From<ApkPermission<'a>> for Permission {
    fn from(permission: ApkPermission<'a>) -> Self {
        Permission {
            description: permission.description.map(String::from),
            icon: permission.icon.map(String::from),
            label: permission.label.map(String::from),
            name: permission.name.map(String::from),
            permission_group: permission.permission_group.map(String::from),
            protection_level: permission.protection_level.map(String::from),
        }
    }
}

#[pymethods]
impl Permission {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            ($field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };
        }
        push_field!(description);
        push_field!(icon);
        push_field!(label);
        push_field!(name);
        push_field!(permission_group);
        push_field!(protection_level);

        format!("Permission({})", parts.join(", "))
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Provider {
    #[pyo3(get)]
    pub authorities: Option<String>,
    #[pyo3(get)]
    pub enabled: Option<String>,
    #[pyo3(get)]
    pub direct_boot_aware: Option<String>,
    #[pyo3(get)]
    pub exported: Option<String>,
    #[pyo3(get)]
    pub grant_uri_permissions: Option<String>,
    #[pyo3(get)]
    pub icon: Option<String>,
    #[pyo3(get)]
    pub init_order: Option<String>,
    #[pyo3(get)]
    pub label: Option<String>,
    #[pyo3(get)]
    pub multiprocess: Option<String>,
    #[pyo3(get)]
    pub name: Option<String>,
    #[pyo3(get)]
    pub permission: Option<String>,
    #[pyo3(get)]
    pub process: Option<String>,
    #[pyo3(get)]
    pub read_permission: Option<String>,
    #[pyo3(get)]
    pub syncable: Option<String>,
    #[pyo3(get)]
    pub write_permission: Option<String>,
}

impl<'a> From<ApkProvider<'a>> for Provider {
    fn from(provider: ApkProvider<'a>) -> Self {
        Provider {
            authorities: provider.authorities.map(String::from),
            enabled: provider.enabled.map(String::from),
            direct_boot_aware: provider.direct_boot_aware.map(String::from),
            exported: provider.exported.map(String::from),
            grant_uri_permissions: provider.grant_uri_permissions.map(String::from),
            icon: provider.icon.map(String::from),
            init_order: provider.init_order.map(String::from),
            label: provider.label.map(String::from),
            multiprocess: provider.multiprocess.map(String::from),
            name: provider.name.map(String::from),
            permission: provider.permission.map(String::from),
            process: provider.process.map(String::from),
            read_permission: provider.read_permission.map(String::from),
            syncable: provider.syncable.map(String::from),
            write_permission: provider.write_permission.map(String::from),
        }
    }
}

#[pymethods]
impl Provider {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            ($field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };
        }

        push_field!(authorities);
        push_field!(enabled);
        push_field!(direct_boot_aware);
        push_field!(exported);
        push_field!(grant_uri_permissions);
        push_field!(icon);
        push_field!(init_order);
        push_field!(label);
        push_field!(multiprocess);
        push_field!(name);
        push_field!(permission);
        push_field!(process);
        push_field!(read_permission);
        push_field!(syncable);
        push_field!(write_permission);

        format!("Provider({})", parts.join(", "))
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct Service {
    #[pyo3(get)]
    description: Option<String>,
    #[pyo3(get)]
    direct_boot_aware: Option<String>,
    #[pyo3(get)]
    enabled: Option<String>,
    #[pyo3(get)]
    exported: Option<String>,
    #[pyo3(get)]
    foreground_service_type: Option<String>,
    #[pyo3(get)]
    icon: Option<String>,
    #[pyo3(get)]
    isolated_process: Option<String>,
    #[pyo3(get)]
    label: Option<String>,
    #[pyo3(get)]
    name: Option<String>,
    #[pyo3(get)]
    permission: Option<String>,
    #[pyo3(get)]
    process: Option<String>,
    #[pyo3(get)]
    stop_with_task: Option<String>,
}

impl<'a> From<ApkService<'a>> for Service {
    fn from(service: ApkService<'a>) -> Self {
        Service {
            description: service.description.map(String::from),
            direct_boot_aware: service.direct_boot_aware.map(String::from),
            enabled: service.enabled.map(String::from),
            exported: service.exported.map(String::from),
            foreground_service_type: service.foreground_service_type.map(String::from),
            icon: service.icon.map(String::from),
            isolated_process: service.isolated_process.map(String::from),
            label: service.label.map(String::from),
            name: service.name.map(String::from),
            permission: service.permission.map(String::from),
            process: service.process.map(String::from),
            stop_with_task: service.stop_with_task.map(String::from),
        }
    }
}

#[pymethods]
impl Service {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            ($field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };
        }
        push_field!(description);
        push_field!(direct_boot_aware);
        push_field!(enabled);
        push_field!(exported);
        push_field!(foreground_service_type);
        push_field!(isolated_process);
        push_field!(name);
        push_field!(permission);
        push_field!(process);
        push_field!(stop_with_task);

        format!("Service({})", parts.join(", "))
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct Receiver {
    #[pyo3(get)]
    pub direct_boot_aware: Option<String>,

    #[pyo3(get)]
    pub enabled: Option<String>,

    #[pyo3(get)]
    pub exported: Option<String>,

    #[pyo3(get)]
    pub icon: Option<String>,

    #[pyo3(get)]
    pub label: Option<String>,

    #[pyo3(get)]
    pub name: Option<String>,

    #[pyo3(get)]
    pub permission: Option<String>,

    #[pyo3(get)]
    pub process: Option<String>,
}

impl<'a> From<ApkReceiver<'a>> for Receiver {
    fn from(receiver: ApkReceiver<'a>) -> Self {
        Receiver {
            direct_boot_aware: receiver.direct_boot_aware.map(String::from),
            enabled: receiver.enabled.map(String::from),
            exported: receiver.exported.map(String::from),
            icon: receiver.icon.map(String::from),
            label: receiver.label.map(String::from),
            name: receiver.name.map(String::from),
            permission: receiver.permission.map(String::from),
            process: receiver.process.map(String::from),
        }
    }
}

#[pymethods]
impl Receiver {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            ($field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };
        }
        push_field!(direct_boot_aware);
        push_field!(enabled);
        push_field!(exported);
        push_field!(icon);
        push_field!(label);
        push_field!(name);
        push_field!(permission);
        push_field!(process);

        format!("Receiver({})", parts.join(", "))
    }
}

#[pyclass(frozen, from_py_object, module = "apk_info._apk_info")]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct Attribution {
    #[pyo3(get)]
    pub tag: Option<String>,

    #[pyo3(get)]
    pub label: Option<String>,
}

impl<'a> From<ApkAttribution<'a>> for Attribution {
    fn from(attribution: ApkAttribution<'a>) -> Self {
        Attribution {
            tag: attribution.tag.map(String::from),
            label: attribution.label.map(String::from),
        }
    }
}

#[pymethods]
impl Attribution {
    fn __repr__(&self) -> String {
        let mut parts = Vec::with_capacity(16);
        macro_rules! push_field {
            ($field:ident) => {
                if let Some(ref v) = self.$field {
                    parts.push(format!(concat!(stringify!($field), "={:?}"), v));
                }
            };
        }
        push_field!(tag);
        push_field!(label);

        format!("Attribution({})", parts.join(", "))
    }
}

#[pyclass(name = "APK", unsendable, module = "apk_info._apk_info")]
struct Apk {
    apkrs: ApkRust,
}

#[pymethods]
impl Apk {
    #[new]
    pub fn new(path: &Bound<'_, PyAny>) -> PyResult<Apk> {
        let resolved: Option<PathBuf> = if let Ok(s) = path.extract::<&str>() {
            Some(PathBuf::from(s))
        } else {
            path.extract::<PathBuf>().ok()
        };

        let path = resolved.ok_or_else(|| PyTypeError::new_err("expected str | PurePath"))?;
        if !path.exists() {
            return Err(PyFileNotFoundError::new_err(format!(
                "file not found: {:?}",
                path
            )));
        }

        let apkrs = ApkRust::new(&path).map_err(|e| APKError::new_err(e.to_string()))?;

        Ok(Apk { apkrs })
    }

    pub fn read(&self, filename: &Bound<'_, PyString>) -> PyResult<(Vec<u8>, FileCompressionType)> {
        let filename = match filename.extract::<&str>() {
            Ok(name) => name,
            Err(_) => return Err(PyValueError::new_err("bad filename")),
        };

        match self.apkrs.read(filename) {
            Ok((data, compression)) => Ok((data, FileCompressionType::from(compression))),
            Err(e) => Err(APKError::new_err(e.to_string())),
        }
    }

    pub fn namelist(&self) -> Vec<&str> {
        self.apkrs.namelist().collect()
    }

    pub fn is_multidex(&self) -> bool {
        self.apkrs.is_multidex()
    }

    pub fn get_xml_string(&self) -> String {
        self.apkrs.get_xml_string()
    }

    pub fn get_resource_value(&self, name: &str) -> Option<String> {
        self.apkrs.get_resource_value(name)
    }

    pub fn get_attribute_value(&self, tag: &str, name: &str) -> Option<String> {
        self.apkrs.get_attribute_value(tag, name)
    }

    pub fn get_all_attribute_values<'a>(&'a self, tag: &'a str, name: &'a str) -> Vec<&'a str> {
        self.apkrs.get_all_attribute_values(tag, name).collect()
    }

    pub fn get_package_name(&self) -> Option<String> {
        self.apkrs.get_package_name()
    }

    pub fn get_shared_user_id(&self) -> Option<String> {
        self.apkrs.get_shared_user_id()
    }

    pub fn get_shared_user_label(&self) -> Option<String> {
        self.apkrs.get_shared_user_label()
    }

    pub fn get_shared_user_max_sdk_version(&self) -> Option<String> {
        self.apkrs.get_shared_user_max_sdk_version()
    }

    pub fn get_version_code(&self) -> Option<String> {
        self.apkrs.get_version_code()
    }

    pub fn get_version_name(&self) -> Option<String> {
        self.apkrs.get_version_name()
    }

    pub fn get_build_version_code(&self) -> Option<String> {
        self.apkrs.get_build_version_code()
    }

    pub fn get_build_version_name(&self) -> Option<String> {
        self.apkrs.get_build_version_name()
    }
    pub fn get_compile_sdk_version(&self) -> Option<String> {
        self.apkrs.get_compile_sdk_version()
    }

    pub fn get_compile_sdk_version_codename(&self) -> Option<String> {
        self.apkrs.get_compile_sdk_version_codename()
    }

    pub fn get_install_location(&self) -> Option<String> {
        self.apkrs.get_install_location()
    }

    pub fn get_application_task_reparenting(&self) -> Option<String> {
        self.apkrs.get_application_task_reparenting()
    }

    pub fn get_application_allow_backup(&self) -> Option<String> {
        self.apkrs.get_application_allow_backup()
    }

    pub fn get_application_category(&self) -> Option<String> {
        self.apkrs.get_application_category()
    }

    pub fn get_application_backup_agent(&self) -> Option<String> {
        self.apkrs.get_application_backup_agent()
    }

    pub fn get_application_debuggable(&self) -> Option<String> {
        self.apkrs.get_application_debuggable()
    }

    pub fn get_application_description(&self) -> Option<String> {
        self.apkrs.get_application_description()
    }

    pub fn get_application_icon(&self) -> Option<String> {
        self.apkrs.get_application_icon()
    }

    pub fn get_application_logo(&self) -> Option<String> {
        self.apkrs.get_application_logo()
    }

    pub fn get_application_label(&self) -> Option<String> {
        self.apkrs.get_application_label()
    }

    pub fn get_application_name(&self) -> Option<String> {
        self.apkrs.get_application_name()
    }

    pub fn get_attributions(&self) -> HashSet<Attribution> {
        self.apkrs
            .get_attributions()
            .map(Attribution::from)
            .collect()
    }

    pub fn get_permissions(&self) -> HashSet<&str> {
        self.apkrs.get_permissions().collect()
    }

    pub fn get_permissions_sdk23(&self) -> HashSet<&str> {
        self.apkrs.get_permissions_sdk23().collect()
    }

    pub fn get_min_sdk_version(&self) -> Option<String> {
        self.apkrs.get_min_sdk_version()
    }

    pub fn get_target_sdk_version(&self) -> u32 {
        self.apkrs.get_target_sdk_version()
    }

    pub fn get_max_sdk_version(&self) -> Option<String> {
        self.apkrs.get_max_sdk_version()
    }

    pub fn get_libraries(&self) -> HashSet<&str> {
        self.apkrs.get_libraries().collect()
    }

    pub fn get_native_libraries(&self) -> HashSet<&str> {
        self.apkrs.get_native_libraries().collect()
    }

    pub fn get_features(&self) -> HashSet<&str> {
        self.apkrs.get_features().collect()
    }

    pub fn is_automotive(&self) -> bool {
        self.apkrs.is_automotive()
    }

    pub fn is_leanback(&self) -> bool {
        self.apkrs.is_leanback()
    }

    pub fn is_wearable(&self) -> bool {
        self.apkrs.is_wearable()
    }

    pub fn is_chromebook(&self) -> bool {
        self.apkrs.is_chromebook()
    }

    pub fn get_declared_permissions(&self) -> HashSet<Permission> {
        self.apkrs
            .get_declared_permissions()
            .map(Permission::from)
            .collect()
    }

    pub fn get_main_activity(&self) -> Option<&str> {
        self.apkrs.get_main_activity()
    }

    // Use a vector instead of a hashset to preserve the order of the found activities
    pub fn get_main_activities(&self) -> Vec<&str> {
        self.apkrs.get_main_activities().collect()
    }

    pub fn get_activities(&self) -> Vec<Activity> {
        self.apkrs.get_activities().map(Activity::from).collect()
    }

    pub fn get_activity_aliases(&self) -> Vec<ActivityAlias> {
        self.apkrs
            .get_activity_aliases()
            .map(ActivityAlias::from)
            .collect()
    }

    pub fn get_services(&self) -> Vec<Service> {
        self.apkrs.get_services().map(Service::from).collect()
    }

    pub fn get_receivers(&self) -> Vec<Receiver> {
        self.apkrs.get_receivers().map(Receiver::from).collect()
    }

    pub fn get_providers(&self) -> Vec<Provider> {
        self.apkrs.get_providers().map(Provider::from).collect()
    }

    pub fn get_signatures<'py>(&self, py: Python<'py>) -> PyResult<Vec<Bound<'py, Signature>>> {
        Ok(self
            .apkrs
            .get_signatures()
            .map_err(|e| APKError::new_err(format!("failed to get signatures: {:?}", e)))?
            .into_iter()
            .filter_map(|x| Signature::from(py, x))
            .collect())
    }

    pub fn get_native_codes(&self) -> Vec<String> {
        self.apkrs.get_native_codes()
    }
}

#[pymodule]
fn apk_info(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
    env_logger::init();

    m.add("APKError", m.py().get_type::<APKError>())?;
    m.add("__version__", env!("CARGO_PKG_VERSION"))?;
    m.add_class::<CertificateInfo>()?;
    m.add_class::<IntentFilter>()?;
    m.add_class::<Activity>()?;
    m.add_class::<ActivityAlias>()?;
    m.add_class::<Permission>()?;
    m.add_class::<Provider>()?;
    m.add_class::<Receiver>()?;
    m.add_class::<Service>()?;
    m.add_class::<Signature>()?;
    m.add_class::<FileCompressionType>()?;

    m.add_class::<Apk>()?;
    Ok(())
}
