use std::sync::Arc;

use crate::value::Value;
use hamelin_lib::tree::ast::expression::{
    BinaryLiteral, Expression, IntervalUnit, RowsLiteral, UnboundRangeLiteral,
};
use hamelin_lib::tree::ast::ops::{BinaryOp, UnaryPostfixOp, UnaryPrefixOp};
use hamelin_lib::tree::builder::{
    boolean, call, decimal_from_parts, double, int, interval, lambda, null, pair, string,
    struct_literal, ArrayLiteralBuilder, BinaryOperatorBuilder, ExpressionBuilder,
    TupleLiteralBuilder, UnaryPostfixOperatorBuilder, UnaryPrefixOperatorBuilder,
};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::tree::typed_ast::expression::TypedExpression;
use hamelin_lib::type_check_expression;

impl From<Value> for Arc<TypedExpression> {
    fn from(value: Value) -> Self {
        Arc::new(TypedExpression::from(value))
    }
}

impl From<Value> for TypedExpression {
    fn from(value: Value) -> Self {
        let ast = match value {
            Value::Boolean(b) => boolean(b).build(),
            Value::Int(i) => int(i).build(),
            Value::Double(d) => double(d).build(),
            Value::String(s) => string(s).build(),
            Value::Binary(b) => Expression::from_kind(BinaryLiteral { value: b }),
            Value::Rows(r) => Expression::from_kind(RowsLiteral { value: r }),
            Value::Interval(duration) => {
                let millis = duration.num_milliseconds();
                interval(millis, IntervalUnit::Millisecond).build()
            }
            Value::CalendarInterval(months) => {
                // CalendarInterval is stored as number of months
                interval(months as i64, IntervalUnit::Month).build()
            }
            Value::Null => null().build(),
            Value::Decimal(decimal) => {
                // Calculate precision from unscaled value, ensuring precision >= scale
                let unscaled_digits = decimal.unscaled.abs().to_string().len() as u32;
                let precision = unscaled_digits.max(decimal.scale as u32);
                decimal_from_parts(decimal.unscaled, precision, decimal.scale as u32).build()
            }
            Value::Array(elements) => {
                let mut builder = ArrayLiteralBuilder::new();
                for elem in elements {
                    let typed_elem = TypedExpression::from(elem);
                    builder = builder.element(typed_elem.ast.as_ref().clone());
                }
                builder.build()
            }
            Value::Tuple(elements) => {
                let mut builder = TupleLiteralBuilder::new();
                for elem in elements {
                    let typed_elem = TypedExpression::from(elem);
                    builder = builder.element(typed_elem.ast.as_ref().clone());
                }
                builder.build()
            }
            Value::Struct(fields) => {
                let mut builder = struct_literal();
                for (sql_name, value) in fields {
                    let typed_expr = TypedExpression::from(value);
                    builder = builder.field(sql_name.name(), typed_expr.ast.as_ref().clone());
                }
                builder.build()
            }
            Value::Timestamp(ts) => {
                // Create ts("ISO_STRING") function call
                let iso_string = ts.instant().to_rfc3339();
                call("ts").arg(string(iso_string)).build()
            }
            Value::Map(map) => {
                // Convert map to array of pairs, then call map() function
                let mut array_builder = ArrayLiteralBuilder::new();
                for (k, v) in map {
                    let key_expr = TypedExpression::from(k);
                    let val_expr = TypedExpression::from(v);
                    let pair_expr =
                        pair(key_expr.ast.as_ref().clone(), val_expr.ast.as_ref().clone());
                    array_builder = array_builder.element(pair_expr);
                }
                call("map").arg(array_builder).build()
            }
            Value::Range(range) => {
                match (&range.lower, &range.upper) {
                    (Some(lower), Some(upper)) => {
                        // Both bounds: lower..upper (binary operator)
                        let left_expr = TypedExpression::from(lower.clone());
                        let right_expr = TypedExpression::from(upper.clone());
                        BinaryOperatorBuilder::new(
                            BinaryOp::Range,
                            left_expr.ast.as_ref().clone(),
                            right_expr.ast.as_ref().clone(),
                        )
                        .build()
                    }
                    (Some(lower), None) => {
                        // Only lower bound: lower.. (unary postfix operator)
                        let operand_expr = TypedExpression::from(lower.clone());
                        UnaryPostfixOperatorBuilder::new(
                            UnaryPostfixOp::Range,
                            operand_expr.ast.as_ref().clone(),
                        )
                        .build()
                    }
                    (None, Some(upper)) => {
                        // Only upper bound: ..upper (unary prefix operator)
                        let operand_expr = TypedExpression::from(upper.clone());
                        UnaryPrefixOperatorBuilder::new(
                            UnaryPrefixOp::Range,
                            operand_expr.ast.as_ref().clone(),
                        )
                        .build()
                    }
                    (None, None) => {
                        // No bounds: .. (unbound range literal)
                        Expression::from_kind(UnboundRangeLiteral {})
                    }
                }
            }
            Value::Variant(json) => {
                // Stringify JSON and call parse_json()
                let json_string = json.to_string();
                call("parse_json").arg(string(json_string)).build()
            }
            Value::Closure(closure) => {
                // Reconstruct the lambda AST with type hints
                closure
                    .lambda
                    .parameters
                    .iter()
                    .fold(lambda(), |b, param| {
                        b.param_with_type(param.name.name(), param.typ.clone())
                    })
                    .body(closure.lambda.body.ast.as_ref().clone())
                    .build()
            }
            Value::Unknown => null().build(),
        };

        type_check_expression(ast, ExpressionTypeCheckOptions::default()).output
    }
}
