//! Type mapping from Nautilus schema types to Python types.
//!
//! Language-independent logic (auto-generation detection, filter operator
//! construction) lives in [`crate::backend::PythonBackend`].  This module
//! keeps only the Python-specific helpers: full-field type composition,
//! base-type extraction, and default-value formatting.

use nautilus_schema::ir::{FieldIr, ResolvedFieldType, ScalarType};
use std::collections::HashMap;

use crate::backend::{FilterOperator, LanguageBackend, PythonBackend};

pub use crate::backend::FilterOperator as FilterOp;

/// Maps a Nautilus scalar type to its Python type representation.
pub fn scalar_to_python_type(scalar: &ScalarType) -> &'static str {
    PythonBackend.scalar_to_type(scalar)
}

/// Maps a field to its complete Python type, including `Optional` and `List` wrappers.
pub fn field_to_python_type(
    field: &FieldIr,
    enums: &HashMap<String, nautilus_schema::ir::EnumIr>,
) -> String {
    let base_type = match &field.field_type {
        ResolvedFieldType::Scalar(scalar) => scalar_to_python_type(scalar).to_string(),
        ResolvedFieldType::Enum { enum_name } => {
            if enums.contains_key(enum_name) {
                enum_name.clone()
            } else {
                "str".to_string()
            }
        }
        ResolvedFieldType::CompositeType { type_name } => type_name.clone(),
        ResolvedFieldType::Relation(rel) => rel.target_model.clone(),
    };

    let with_array = if field.is_array {
        format!("List[{}]", base_type)
    } else {
        base_type
    };

    // Relations (single, non-array) are always Optional for lazy-loading.
    let is_relation = matches!(&field.field_type, ResolvedFieldType::Relation(_));

    // Auto-generated fields (uuid(), now(), autoincrement()) are Optional even
    // if required, because they are provided by the database, not the caller.
    if !field.is_required || is_auto_generated(field) || (is_relation && !field.is_array) {
        if !field.is_array {
            format!("Optional[{}]", with_array)
        } else {
            with_array
        }
    } else {
        with_array
    }
}

/// Gets the base Python type for a field without `Optional` or `List` wrappers.
pub fn get_base_python_type(
    field: &FieldIr,
    enums: &HashMap<String, nautilus_schema::ir::EnumIr>,
) -> String {
    PythonBackend.get_base_type(field, enums)
}

/// Checks if a field is auto-generated by the database (autoincrement, uuid, or now).
///
/// # Note — divergence from the Rust `is_auto_generated` in `type_helpers.rs`
///
/// The Python version additionally treats `now()` as auto-generated because
/// Python callers never supply a timestamp; it is always provided server-side.
/// The Rust generated code does expose `now()` fields in the create-input
/// because callers may want to override the default.
pub fn is_auto_generated(field: &FieldIr) -> bool {
    PythonBackend.is_auto_generated(field)
}

/// Gets the Python default value for a field, if any.
pub fn get_default_value(field: &FieldIr) -> Option<String> {
    PythonBackend.get_default_value(field)
}

/// Gets the available filter operators for a given scalar type.
pub fn get_filter_operators_for_scalar(scalar: &ScalarType) -> Vec<FilterOperator> {
    PythonBackend.get_filter_operators_for_scalar(scalar)
}

/// Gets filter operators for a field, considering its type (scalar, enum, or relation).
pub fn get_filter_operators_for_field(
    field: &FieldIr,
    enums: &HashMap<String, nautilus_schema::ir::EnumIr>,
) -> Vec<FilterOperator> {
    PythonBackend.get_filter_operators_for_field(field, enums)
}
