pub mod arrow_interop;
pub mod backtest;
pub mod data;
pub mod ingest;
pub mod mock_store;
pub mod portfolio;
pub mod research;
pub mod result;
pub mod strategy;

use bt_kernel::BtError;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;

use backtest::{
    load_and_align, load_parquet_as_aligned, run, run_batch, run_batch_lite, run_json,
    run_on_aligned, run_sweep, run_sweep_lite, run_with_parquet, PyAlignedData, PyBatchResultLite,
};
use data::PyDataStore;
use research::{
    py_replay, py_run_monte_carlo, py_run_stability, py_run_sweep_2d, py_run_walk_forward,
};
use result::PyBacktestResult;
use strategy::compile_strategy_json;

/// Activate a Pro license key.
/// Usage from Python: `bt.activate("YOUR_LICENSE_KEY")`
#[pyfunction]
fn activate(license_key: &str) -> PyResult<String> {
    match bt_license::activate(license_key) {
        Ok(true) => Ok("Pro license activated successfully.".to_string()),
        _ => Err(PyValueError::new_err(
            "Invalid or expired license key. Running in Community mode.",
        )),
    }
}

/// Returns license info as a dict: {"tier": "Pro"|"Community", "email": "..."|null}
#[pyfunction]
fn license_info() -> PyResult<(String, Option<String>)> {
    let (tier, email) = bt_license::license_info();
    Ok((tier, email))
}

#[pymodule]
fn _native(_py: Python<'_>, module: &Bound<'_, PyModule>) -> PyResult<()> {
    pyo3_log::try_init().ok();
    // Initialize license guard (loads from disk if previously activated)
    bt_license::init_guard(None);
    module.add_class::<PyDataStore>()?;
    module.add_class::<PyBacktestResult>()?;
    module.add_class::<PyAlignedData>()?;
    module.add_function(wrap_pyfunction!(compile_strategy_json, module)?)?;
    module.add_function(wrap_pyfunction!(run_json, module)?)?;
    module.add_function(wrap_pyfunction!(run, module)?)?;
    module.add_function(wrap_pyfunction!(run_sweep, module)?)?;
    module.add_function(wrap_pyfunction!(run_sweep_lite, module)?)?;
    module.add_function(wrap_pyfunction!(run_with_parquet, module)?)?;
    module.add_function(wrap_pyfunction!(run_batch, module)?)?;
    module.add_function(wrap_pyfunction!(run_batch_lite, module)?)?;
    module.add_function(wrap_pyfunction!(load_and_align, module)?)?;
    module.add_function(wrap_pyfunction!(run_on_aligned, module)?)?;
    module.add_function(wrap_pyfunction!(load_parquet_as_aligned, module)?)?;
    module.add_class::<PyBatchResultLite>()?;
    // Research functions
    module.add_function(wrap_pyfunction!(py_run_walk_forward, module)?)?;
    module.add_function(wrap_pyfunction!(py_run_sweep_2d, module)?)?;
    module.add_function(wrap_pyfunction!(py_run_stability, module)?)?;
    module.add_function(wrap_pyfunction!(py_replay, module)?)?;
    module.add_function(wrap_pyfunction!(py_run_monte_carlo, module)?)?;
    module.add_function(wrap_pyfunction!(activate, module)?)?;
    module.add_function(wrap_pyfunction!(license_info, module)?)?;
    // Portfolio
    module.add_function(wrap_pyfunction!(portfolio::run_portfolio, module)?)?;
    // Ingest
    module.add_function(wrap_pyfunction!(ingest::py_ingest, module)?)?;
    Ok(())
}

pub(crate) fn map_bt_error(err: BtError) -> PyErr {
    PyValueError::new_err(err.to_string())
}

pub(crate) fn map_json_error(err: serde_json::Error) -> PyErr {
    PyValueError::new_err(format!("invalid json payload: {err}"))
}
