//! Translation implementations for JSON functions

use ordermap::OrderMap;

use crate::TranslationRegistry;
use hamelin_lib::func::defs::{ParseJsonString, ParseJsonVariant, ToJsonString, Typeof};
use hamelin_lib::sql::expression::apply::FunctionCallApply;
use hamelin_lib::sql::expression::identifier::SimpleIdentifier;
use hamelin_lib::sql::expression::literal::{RowLiteral, StringLiteral};
use hamelin_lib::sql::expression::Cast;
use hamelin_lib::sql::types::{SQLBaseType, SQLRowType};

/// Register all JSON function translations.
pub fn register(registry: &mut TranslationRegistry) {
    // parse_json(string) - translate to json_extract(json, '$')
    registry.register::<ParseJsonString>(|_, mut bindings| {
        let json = bindings.take()?;
        Ok(
            FunctionCallApply::with_two("json_extract", json.sql, StringLiteral::new("$").into())
                .into(),
        )
    });

    // parse_json(variant) - pass through (already parsed)
    registry.register::<ParseJsonVariant>(|_, mut bindings| bindings.take().map(|t| t.sql));

    // to_json_string(variant) - translate to json_format()
    registry.register::<ToJsonString>(|_, mut bindings| {
        let json = bindings.take()?;
        Ok(FunctionCallApply::with_one("json_format", json.sql).into())
    });

    // typeof(x) - returns struct with hamelin_type (static) and sql_type (dynamic)
    // Produces: CAST(ROW(hamelin_type_val, typeof(expr)) AS ROW(hamelin_type VARCHAR, sql_type VARCHAR))
    registry.register::<Typeof>(|_, mut bindings| {
        let expr = bindings.take()?;

        // Build the ROW literal with values
        let row_literal = RowLiteral::new(vec![
            StringLiteral::new(&expr.typ.to_string()).into(),
            FunctionCallApply::with_one("typeof", expr.sql).into(),
        ]);

        // Build the ROW type: ROW(hamelin_type VARCHAR, sql_type VARCHAR)
        let mut fields = OrderMap::new();
        fields.insert(
            SimpleIdentifier::new("hamelin_type"),
            SQLBaseType::VarChar.into(),
        );
        fields.insert(
            SimpleIdentifier::new("sql_type"),
            SQLBaseType::VarChar.into(),
        );
        let row_type = SQLRowType::new(fields);

        // Build CAST(ROW(...) AS ROW(...))
        Ok(Cast::new(row_literal.into(), row_type.into()).into())
    });
}
