use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use serde_json::Value;

use frisk_core::value_utils::redact_value::{redact_value as core_redact_value, RedactOption};

#[pyfunction]
pub fn redact_dictionary<'py>(
    py: Python<'py>,
    data: &Bound<'_, PyAny>,
    redact_option: &Bound<'_, PyAny>,
) -> PyResult<(Bound<'py, PyAny>, Vec<String>)> {
    // Convert Python -> serde_json::Value (no string)
    let json_in: Value = pythonize::depythonize(data)
        .map_err(|e| PyTypeError::new_err(format!("data must be JSON-like: {e}")))?;

    // Parse redact option (bool or list[str])
    let opt = parse_redact_option(redact_option)?;

    // Call core
    let (json_out, hits) = core_redact_value(&json_in, &opt);

    // Convert serde_json::Value -> Python (no string)
    let py_out = pythonize::pythonize(py, &json_out)
        .map_err(|e| PyTypeError::new_err(format!("failed converting result to python: {e}")))?;

    Ok((py_out, hits))
}

// Convert Python bool or list[str] into RedactOption enum
pub fn parse_redact_option(obj: &Bound<'_, PyAny>) -> PyResult<RedactOption> {
    if obj.is_none() {
        return Ok(RedactOption::Paths(vec![]));
    }

    if let Ok(b) = obj.extract::<bool>() {
        return Ok(if b {
            RedactOption::All
        } else {
            RedactOption::Paths(vec![])
        });
    }

    if let Ok(v) = obj.extract::<Vec<String>>() {
        return Ok(RedactOption::Paths(v));
    }

    Err(PyTypeError::new_err(
        "redact_option must be a bool or list[str]",
    ))
}
