mod about_json;
mod channel;
mod error;
mod explicit_environment_spec;
mod generic_virtual_package;
mod index;
mod installer;
mod lock;
mod match_spec;
mod meta;
mod nameless_match_spec;
mod networking;
mod no_arch_type;
mod package_name;
mod package_name_matcher;
mod package_streaming;
mod paths_json;
mod platform;
mod prefix_paths;
#[cfg(feature = "pty")]
mod pty;
mod record;
mod repo_data;
mod shell;
mod solver;
mod utils;
mod version;
mod virtual_package;

mod exceptions;
mod index_json;
mod run_exports_json;

use std::ops::Deref;

use about_json::PyAboutJson;
use channel::{PyChannel, PyChannelConfig, PyChannelPriority};
use error::PyRattlerError;
use exceptions::{
    ActivationError, ActivationScriptFormatError, AuthenticationStorageError, CacheDirError,
    ConversionError, ConvertSubdirError, DetectVirtualPackageError, EnvironmentCreationError,
    FetchRepoDataError, InvalidChannelError, InvalidHeaderNameError, InvalidHeaderValueError,
    InvalidMatchSpecError, InvalidPackageNameError, InvalidUrlError, InvalidVersionError,
    InvalidVersionSpecError, IoError, LinkError, PackageNameMatcherParseError, ParseArchError,
    ParseCondaLockError, ParseExplicitEnvironmentSpecError, ParsePlatformError, RequirementError,
    ShellError, SolverError, TransactionError, ValidatePackageRecordsError, VersionBumpError,
    VersionExtendError,
};
use explicit_environment_spec::{PyExplicitEnvironmentEntry, PyExplicitEnvironmentSpec};
use generic_virtual_package::PyGenericVirtualPackage;
use index::{py_index_fs, py_index_s3};
use index_json::PyIndexJson;
use installer::py_install;
use lock::{
    PyEnvironment, PyLockChannel, PyLockFile, PyLockedPackage, PyPackageHashes, PyPypiPackageData,
    PyPypiPackageEnvironmentData,
};
use match_spec::PyMatchSpec;
use meta::get_rattler_version;
use nameless_match_spec::PyNamelessMatchSpec;
use networking::middleware::{
    PyAddHeadersMiddleware, PyAuthenticationMiddleware, PyGCSMiddleware, PyMirrorMiddleware,
    PyOciMiddleware, PyRetryMiddleware, PyS3Config, PyS3Middleware,
};
use networking::{client::PyClientWithMiddleware, py_fetch_repo_data};
use no_arch_type::PyNoArchType;
use package_name::PyPackageName;
use package_name_matcher::PyPackageNameMatcher;
use paths_json::{PyFileMode, PyPathType, PyPathsEntry, PyPathsJson, PyPrefixPlaceholder};
use platform::{PyArch, PyPlatform};
use prefix_paths::{PyPrefixPathType, PyPrefixPaths, PyPrefixPathsEntry};
use pyo3::prelude::*;
use record::{PyLink, PyRecord};
use repo_data::{
    gateway::{PyFetchRepoDataOptions, PyGateway, PySourceConfig},
    patch_instructions::PyPatchInstructions,
    sparse::{PyPackageFormatSelection, PySparseRepoData},
    PyRepoData,
};
use run_exports_json::PyRunExportsJson;
use shell::{PyActivationResult, PyActivationVariables, PyActivator, PyShellEnum};
use solver::{py_solve, py_solve_with_sparse_repodata, PyMinimumAgeConfig};
use version::{PyVersion, PyVersionSpec};
use virtual_package::{PyOverride, PyVirtualPackage, PyVirtualPackageOverrides};

#[cfg(feature = "pty")]
use pty::{PyPtyProcess, PyPtyProcessOptions, PyPtySession};

/// A struct to make it easy to wrap a type as a python type.
#[repr(transparent)]
#[derive(Clone)]
pub struct Wrap<T>(pub T);

impl<T> Deref for Wrap<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[pymodule]
fn rattler<'py>(py: Python<'py>, m: Bound<'py, PyModule>) -> PyResult<()> {
    m.add_class::<PyVersion>()?;
    m.add_class::<PyVersionSpec>()?;

    m.add_class::<PyMatchSpec>()?;
    m.add_class::<PyNamelessMatchSpec>()?;

    m.add_class::<PyPackageName>()?;
    m.add_class::<PyPackageNameMatcher>()?;

    m.add_class::<PyChannel>()?;
    m.add_class::<PyChannelConfig>()?;
    m.add_class::<PyChannelPriority>()?;
    m.add_class::<PyPlatform>()?;
    m.add_class::<PyArch>()?;

    m.add_class::<PyMirrorMiddleware>()?;
    m.add_class::<PyAuthenticationMiddleware>()?;
    m.add_class::<PyOciMiddleware>()?;
    m.add_class::<PyGCSMiddleware>()?;
    m.add_class::<PyS3Middleware>()?;
    m.add_class::<PyS3Config>()?;
    m.add_class::<PyRetryMiddleware>()?;
    m.add_class::<PyAddHeadersMiddleware>()?;
    m.add_class::<PyClientWithMiddleware>()?;

    // Shell activation things
    m.add_class::<PyActivationVariables>()?;
    m.add_class::<PyActivationResult>()?;
    m.add_class::<PyShellEnum>()?;
    m.add_class::<PyActivator>()?;

    // PTY (pseudoterminal) support - optional feature, Unix only
    #[cfg(feature = "pty")]
    {
        m.add_class::<PyPtyProcess>()?;
        m.add_class::<PyPtyProcessOptions>()?;
        m.add_class::<PyPtySession>()?;
    }

    m.add_class::<PySparseRepoData>()?;
    m.add_class::<PyPackageFormatSelection>()?;
    m.add_class::<PyRepoData>()?;
    m.add_class::<PyPatchInstructions>()?;
    m.add_class::<PyGateway>()?;
    m.add_class::<PySourceConfig>()?;
    m.add_class::<PyFetchRepoDataOptions>()?;

    m.add_class::<PyRecord>()?;
    m.add_class::<PyLink>()?;

    m.add_function(wrap_pyfunction!(py_fetch_repo_data, &m)?)?;
    m.add_class::<PyGenericVirtualPackage>()?;
    m.add_class::<PyOverride>()?;
    m.add_class::<PyVirtualPackageOverrides>()?;
    m.add_class::<PyVirtualPackage>()?;
    m.add_class::<PyPrefixPathsEntry>()?;
    m.add_class::<PyPrefixPathType>()?;
    m.add_class::<PyPrefixPaths>()?;

    m.add_class::<PyNoArchType>()?;

    m.add_class::<PyLockFile>()?;
    m.add_class::<PyEnvironment>()?;
    m.add_class::<PyLockChannel>()?;
    m.add_class::<PyLockedPackage>()?;
    m.add_class::<PyPypiPackageData>()?;
    m.add_class::<PyPypiPackageEnvironmentData>()?;
    m.add_class::<PyPackageHashes>()?;

    m.add_class::<PyAboutJson>()?;

    m.add_class::<PyRunExportsJson>()?;
    m.add_class::<PyPathsJson>()?;
    m.add_class::<PyPathsEntry>()?;
    m.add_class::<PyPathType>()?;
    m.add_class::<PyPrefixPlaceholder>()?;
    m.add_class::<PyFileMode>()?;
    m.add_class::<PyIndexJson>()?;

    m.add_class::<PyMinimumAgeConfig>()?;
    m.add_function(wrap_pyfunction!(py_solve, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(py_solve_with_sparse_repodata, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(get_rattler_version, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(py_install, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(py_index_fs, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(py_index_s3, &m).unwrap())?;

    m.add_function(wrap_pyfunction!(package_streaming::extract_tar_bz2, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(package_streaming::extract, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(package_streaming::download_to_path, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(package_streaming::download_bytes, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(package_streaming::download_to_writer, &m).unwrap())?;
    m.add_function(wrap_pyfunction!(package_streaming::download_and_extract, &m).unwrap())?;
    m.add_function(
        wrap_pyfunction!(package_streaming::fetch_raw_package_file_from_url, &m).unwrap(),
    )?;

    // Explicit environment specification
    m.add_class::<PyExplicitEnvironmentSpec>()?;
    m.add_class::<PyExplicitEnvironmentEntry>()?;

    // Exceptions
    m.add("InvalidVersionError", py.get_type::<InvalidVersionError>())?;
    m.add(
        "InvalidVersionSpecError",
        py.get_type::<InvalidVersionSpecError>(),
    )?;
    m.add(
        "InvalidMatchSpecError",
        py.get_type::<InvalidMatchSpecError>(),
    )?;
    m.add(
        "InvalidPackageNameError",
        py.get_type::<InvalidPackageNameError>(),
    )?;
    m.add(
        "PackageNameMatcherParseError",
        py.get_type::<PackageNameMatcherParseError>(),
    )?;
    m.add("InvalidUrlError", py.get_type::<InvalidUrlError>())?;
    m.add("InvalidChannelError", py.get_type::<InvalidChannelError>())?;
    m.add("ActivationError", py.get_type::<ActivationError>())?;
    m.add(
        "ActivationScriptFormatError",
        py.get_type::<ActivationScriptFormatError>(),
    )?;
    m.add("ParsePlatformError", py.get_type::<ParsePlatformError>())?;
    m.add("ParseArchError", py.get_type::<ParseArchError>())?;
    m.add("FetchRepoDataError", py.get_type::<FetchRepoDataError>())?;
    m.add("CacheDirError", py.get_type::<CacheDirError>())?;
    m.add(
        "DetectVirtualPackageError",
        py.get_type::<DetectVirtualPackageError>(),
    )?;
    m.add("IoError", py.get_type::<IoError>())?;
    m.add("SolverError", py.get_type::<SolverError>())?;
    m.add("TransactionError", py.get_type::<TransactionError>())?;
    m.add("LinkError", py.get_type::<LinkError>())?;
    m.add("ConvertSubdirError", py.get_type::<ConvertSubdirError>())?;
    m.add("VersionBumpError", py.get_type::<VersionBumpError>())?;
    m.add("VersionExtendError", py.get_type::<VersionExtendError>())?;
    m.add("ParseCondaLockError", py.get_type::<ParseCondaLockError>())?;
    m.add("ConversionError", py.get_type::<ConversionError>())?;
    m.add("RequirementError", py.get_type::<RequirementError>())?;
    m.add(
        "EnvironmentCreationError",
        py.get_type::<EnvironmentCreationError>(),
    )?;
    m.add(
        "ExtractError",
        py.get_type::<crate::exceptions::ExtractError>(),
    )?;
    m.add(
        "GatewayError",
        py.get_type::<crate::exceptions::GatewayError>(),
    )?;
    m.add(
        "InstallerError",
        py.get_type::<crate::exceptions::InstallerError>(),
    )?;
    m.add(
        "ParseExplicitEnvironmentSpecError",
        py.get_type::<ParseExplicitEnvironmentSpecError>(),
    )?;
    m.add(
        "ValidatePackageRecordsError",
        py.get_type::<ValidatePackageRecordsError>(),
    )?;
    m.add(
        "AuthenticationStorageError",
        py.get_type::<AuthenticationStorageError>(),
    )?;
    m.add("ShellError", py.get_type::<ShellError>())?;
    m.add(
        "InvalidHeaderNameError",
        py.get_type::<InvalidHeaderNameError>(),
    )?;
    m.add(
        "InvalidHeaderValueError",
        py.get_type::<InvalidHeaderValueError>(),
    )?;

    Ok(())
}
