use std::{ops::RangeInclusive, rc::Rc};

use thiserror::Error;

use crate::{
    antlr::{
        hamelinparser::{
            ArrayLiteralContext, ArrayLiteralContextAttrs, BinaryLiteralContext,
            BinaryOperatorContext, BooleanLiteralContext, BooleanLiteralContextAttrs, CastContext,
            ColumnReferenceAltContext, ColumnReferenceAltContextAttrs, ColumnReferenceContextAttrs,
            DecimalLiteralContext, DerefContext, ExpressionContextAll, ExpressionEOFContextAttrs,
            FunctionCallContext, FunctionCallContextAttrs, IndexAccessContext,
            IntegerLiteralContext, IntervalLiteralContext, IntervalLiteralContextAttrs,
            NamedArgumentContextAttrs, NullLiteralContext, NumberContextAll,
            NumericLiteralContextAttrs, PairLiteralContext, ParenthesizedExpressionContextAttrs,
            PositionalArgumentContextAttrs, RowsLiteralContext, ScientificLiteralContext,
            StringContextAll, StringLiteralContext, StringLiteralContextAttrs,
            StructLiteralContext, StructLiteralContextAttrs, TsTruncContext, TsTruncContextAttrs,
            TupleLiteralContext, TupleLiteralContextAttrs, UnaryPostfixOperatorContext,
            UnaryPostfixOperatorContextAttrs, UnaryPrefixOperatorContext,
            UnaryPrefixOperatorContextAttrs, UnboundRangeLiteralContext,
        },
        interval,
    },
    err::{TranslationError, TranslationErrors},
    resilient_parse_expression,
    tree::{
        ast::ExpressionTranslationOptions,
        ast::{
            context::{FromCst, ParseContext, TryFromCst},
            identifier::{Identifier, ParsedSimpleIdentifier, SimpleIdentifier},
            ops::{BinaryOp, UnaryPostfixOp, UnaryPrefixOp},
            IntoTyped, ParseWithErrors,
        },
        builder::IntoExpressionBuilder,
        typed_ast::expression::TypedExpression,
    },
    types::Type,
};
use antlr_rust::tree::ParseTree;
use derive_more::derive::{From, TryUnwrap};
use vecmap::VecMap;

use super::node::{Span, Spannable};

#[derive(Debug, Clone)]
pub struct Expression {
    pub span: Span,
    pub kind: ExpressionKind,
}

impl Expression {
    pub fn from_kind<T>(kind: T) -> Self
    where
        T: Into<ExpressionKind>,
    {
        Self {
            span: Span::NONE,
            kind: kind.into(),
        }
    }
}

impl ParseWithErrors for Expression {
    fn parse_with_errors(input: impl Into<String>) -> (Self, TranslationErrors) {
        let mut ctx = ParseContext::new();
        match resilient_parse_expression(input.into()) {
            Ok((csteof, parse_errors)) => match csteof.expression() {
                Some(cst) => {
                    let ast = Self::from_cst_with_context(cst, &mut ctx);
                    let mut errors = ctx.take_errors();
                    errors.extend(parse_errors);
                    (ast, errors)
                }
                None => {
                    let err = ctx.error("parse tree did not contain an expression").emit();
                    let mut errors = ctx.take_errors();
                    errors.extend(parse_errors);
                    (Self::from_kind(ErrorExpression { error: err }), errors)
                }
            },
            Err(e) => {
                let err = ctx
                    .error("fatal error initializing parser")
                    .with_source_boxed(e.into())
                    .emit();
                let errors = ctx.take_errors();
                (Self::from_kind(ErrorExpression { error: err }), errors)
            }
        }
    }
}

impl Spannable for Expression {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        self.span.to_range()
    }
}

impl From<ExpressionKind> for Expression {
    fn from(kind: ExpressionKind) -> Self {
        Self {
            span: Span::NONE,
            kind,
        }
    }
}

impl From<Identifier> for Expression {
    fn from(ident: Identifier) -> Self {
        ident.into_expression_builder().build()
    }
}

impl PartialEq for Expression {
    fn eq(&self, other: &Self) -> bool {
        // Compare only the semantic content (kind), ignoring CST
        self.kind == other.kind
    }
}

#[derive(Debug, Clone, PartialEq, From, TryUnwrap)]
pub enum ExpressionKind {
    IntLiteral(IntLiteral),
    DecimalLiteral(DecimalLiteral),
    ScientificLiteral(ScientificLiteral),
    BooleanLiteral(BooleanLiteral),
    StringLiteral(StringLiteral),
    BinaryLiteral(BinaryLiteral),
    NullLiteral(NullLiteral),
    ArrayLiteral(ArrayLiteral),
    TupleLiteral(TupleLiteral),
    PairLiteral(PairLiteral),
    StructLiteral(StructLiteral),
    ColumnReference(ColumnReference),
    UnaryPrefixOperator(UnaryPrefixOperator),
    UnaryPostfixOperator(UnaryPostfixOperator),
    BinaryOperator(BinaryOperator),
    FunctionCall(FunctionCall),
    IndexAccess(IndexAccess),
    FieldLookup(FieldLookup),
    Cast(Cast),
    IntervalLiteral(IntervalLiteral),
    TsTrunc(TsTrunc),
    RowsLiteral(RowsLiteral),
    UnboundRangeLiteral(UnboundRangeLiteral),
    Lambda(Lambda),
    Error(ErrorExpression),
}

impl From<Rc<ExpressionContextAll<'static>>> for Expression {
    fn from(cst: Rc<ExpressionContextAll<'static>>) -> Self {
        let mut ctx = ParseContext::new();
        Self::from_cst_with_context(cst, &mut ctx)
    }
}

impl FromCst<Rc<ExpressionContextAll<'static>>> for Expression {
    fn from_cst_with_context(
        cst: Rc<ExpressionContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        /// Helper function for types that implement FromCst (recursively construct Expressions)
        fn helper_cst<T, C>(parse_ctx: C, ctx: &mut ParseContext) -> ExpressionKind
        where
            T: FromCst<C> + Into<ExpressionKind>,
        {
            T::from_cst_with_context(parse_ctx, ctx).into()
        }

        /// Helper function for types that use TryFromCst (fallible literals)
        fn helper_try<T, C>(parse_ctx: C, ctx: &mut ParseContext) -> ExpressionKind
        where
            T: TryFromCst<C> + Into<ExpressionKind>,
        {
            match T::try_from_cst_with_context(parse_ctx, ctx) {
                Ok(r) => r.into(),
                Err(error) => ErrorExpression { error }.into(),
            }
        }

        let kind = match cst.as_ref() {
            ExpressionContextAll::NumericLiteralContext(num_ctx) => {
                if let Some(number_ctx) = num_ctx.number() {
                    match number_ctx.as_ref() {
                        NumberContextAll::IntegerLiteralContext(int_ctx) => {
                            helper_try::<IntLiteral, _>(int_ctx, ctx)
                        }
                        NumberContextAll::DecimalLiteralContext(dec_ctx) => {
                            helper_try::<DecimalLiteral, _>(dec_ctx, ctx)
                        }
                        NumberContextAll::ScientificLiteralContext(sci_ctx) => {
                            helper_try::<ScientificLiteral, _>(sci_ctx, ctx)
                        }
                        NumberContextAll::Error(_) => {
                            let err = ctx.error("numeric parse error").at(cst.as_ref()).emit();
                            ErrorExpression { error: err }.into()
                        }
                    }
                } else {
                    let err = ctx.error("missing number context").at(cst.as_ref()).emit();
                    ErrorExpression { error: err }.into()
                }
            }
            ExpressionContextAll::BooleanLiteralContext(bool_ctx) => {
                helper_try::<BooleanLiteral, _>(bool_ctx, ctx)
            }
            ExpressionContextAll::StringLiteralContext(str_ctx) => {
                helper_try::<StringLiteral, _>(str_ctx, ctx)
            }
            ExpressionContextAll::BinaryLiteralContext(bin_ctx) => {
                helper_try::<BinaryLiteral, _>(bin_ctx, ctx)
            }
            ExpressionContextAll::NullLiteralContext(null_ctx) => {
                helper_try::<NullLiteral, _>(null_ctx, ctx)
            }
            ExpressionContextAll::PairLiteralContext(pair_ctx) => {
                helper_cst::<PairLiteral, _>(pair_ctx, ctx)
            }
            ExpressionContextAll::RowsLiteralContext(rows_ctx) => {
                helper_try::<RowsLiteral, _>(rows_ctx, ctx)
            }
            ExpressionContextAll::UnboundRangeLiteralContext(range_ctx) => {
                helper_try::<UnboundRangeLiteral, _>(range_ctx, ctx)
            }
            ExpressionContextAll::StructLiteralContext(struct_ctx) => {
                helper_cst::<StructLiteral, _>(struct_ctx, ctx)
            }
            ExpressionContextAll::ColumnReferenceAltContext(col_ctx) => {
                helper_try::<ColumnReference, _>(col_ctx, ctx)
            }
            ExpressionContextAll::DerefContext(deref_ctx) => {
                helper_try::<FieldLookup, _>(deref_ctx, ctx)
            }
            ExpressionContextAll::ArrayLiteralContext(arr_ctx) => {
                helper_cst::<ArrayLiteral, _>(arr_ctx, ctx)
            }
            ExpressionContextAll::TsTruncContext(ts_ctx) => helper_cst::<TsTrunc, _>(ts_ctx, ctx),
            ExpressionContextAll::TsTruncTimestampLiteralContext(ts_lit_ctx) => {
                let err = ctx
                    .error("ts_trunc timestamp literal not yet implemented")
                    .at(ts_lit_ctx)
                    .emit();
                ErrorExpression { error: err }.into()
            }
            ExpressionContextAll::UnaryPrefixOperatorContext(unary_ctx) => {
                helper_cst::<UnaryPrefixOperator, _>(unary_ctx, ctx)
            }
            ExpressionContextAll::IndexAccessContext(idx_ctx) => {
                helper_cst::<IndexAccess, _>(idx_ctx, ctx)
            }
            ExpressionContextAll::CastContext(cast_ctx) => helper_cst::<Cast, _>(cast_ctx, ctx),
            ExpressionContextAll::UnaryPostfixOperatorContext(postfix_ctx) => {
                helper_cst::<UnaryPostfixOperator, _>(postfix_ctx, ctx)
            }
            ExpressionContextAll::TupleLiteralContext(tuple_ctx) => {
                helper_cst::<TupleLiteral, _>(tuple_ctx, ctx)
            }
            ExpressionContextAll::ParenthesizedExpressionContext(paren_ctx) => {
                // Parentheses are purely syntactic - unwrap to inner expression for AST
                if let Some(inner_ctx) = paren_ctx.expression() {
                    return Self::from_cst_with_context(inner_ctx, ctx);
                } else {
                    let err = ctx
                        .error("missing inner expression in parenthesized expression")
                        .at(paren_ctx)
                        .emit();
                    return Self::from_kind(ErrorExpression { error: err });
                }
            }
            ExpressionContextAll::BinaryOperatorContext(bin_op_ctx) => {
                helper_cst::<BinaryOperator, _>(bin_op_ctx, ctx)
            }
            ExpressionContextAll::FunctionCallContext(fn_ctx) => {
                helper_try::<FunctionCall, _>(fn_ctx, ctx)
            }
            ExpressionContextAll::IntervalLiteralContext(interval_ctx) => {
                helper_try::<IntervalLiteral, _>(interval_ctx, ctx)
            }
            ExpressionContextAll::Error(_err_ctx) => {
                let err = ctx.error("parse error").at(cst.as_ref()).emit();
                ErrorExpression { error: err }.into()
            }
        };

        Expression {
            span: interval(cst.as_ref()).into(),
            kind,
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct IntLiteral {
    pub int: i64,
}

impl TryFromCst<&IntegerLiteralContext<'static>> for IntLiteral {
    fn try_from_cst_with_context(
        value: &IntegerLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let s = value.get_text();

        let maybe_int = if s.starts_with("0x") || s.starts_with("0X") {
            i64::from_str_radix(&s[2..], 16)
        } else if s.starts_with("0b") || s.starts_with("0B") {
            i64::from_str_radix(&s[2..], 2)
        } else if s.starts_with("0o") || s.starts_with("0O") {
            i64::from_str_radix(&s[2..], 8)
        } else {
            s.parse::<i64>()
        };

        let int = maybe_int.map_err(|e| {
            ctx.error("invalid integer literal")
                .at(value)
                .with_source_boxed(Box::new(e))
                .emit()
        })?;

        Ok(Self { int })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct DecimalLiteral {
    pub unscaled_value: i128,
    pub precision: u32,
    pub scale: u32,
}

impl TryFromCst<&DecimalLiteralContext<'static>> for DecimalLiteral {
    fn try_from_cst_with_context(
        value: &DecimalLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let s = value.get_text();

        // Find the decimal point
        let decimal_pos = s.find('.').ok_or_else(|| {
            ctx.error("decimal literal missing decimal point")
                .at(value)
                .emit()
        })?;

        let integer_part = &s[..decimal_pos];
        let fractional_part = &s[decimal_pos + 1..];

        let scale = fractional_part.len() as u32;
        let precision = s.replace('.', "").len() as u32;

        // Combine integer and fractional parts
        let combined = format!("{}{}", integer_part, fractional_part);
        let unscaled_value = combined.parse::<i128>().map_err(|e| {
            ctx.error("invalid decimal literal")
                .at(value)
                .with_source_boxed(Box::new(e))
                .emit()
        })?;

        Ok(Self {
            unscaled_value,
            precision,
            scale,
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ScientificLiteral {
    pub value: f64,
}

impl TryFromCst<&ScientificLiteralContext<'static>> for ScientificLiteral {
    fn try_from_cst_with_context(
        value: &ScientificLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let s = value.get_text();
        let float_value = s.parse::<f64>().map_err(|e| {
            ctx.error("invalid scientific literal")
                .at(value)
                .with_source_boxed(Box::new(e))
                .emit()
        })?;

        Ok(Self { value: float_value })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct BooleanLiteral {
    pub value: bool,
}

impl TryFromCst<&BooleanLiteralContext<'static>> for BooleanLiteral {
    fn try_from_cst_with_context(
        value: &BooleanLiteralContext<'static>,
        _ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        Ok(Self {
            value: value.TRUE().is_some(),
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct StringLiteral {
    pub value: String,
}

impl TryFromCst<&StringLiteralContext<'static>> for StringLiteral {
    fn try_from_cst_with_context(
        value: &StringLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let string_ctx = value
            .string()
            .ok_or_else(|| ctx.error("missing string context").at(value).emit())?;

        StringLiteral::try_from_cst_with_context(&string_ctx, ctx)
    }
}

impl TryFromCst<&Rc<StringContextAll<'static>>> for StringLiteral {
    fn try_from_cst_with_context(
        value: &Rc<StringContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let text = value.get_text();

        /// Helper function to process literal text by removing quotes and handling escapes
        fn literal_text(text: &str, quote: &str) -> String {
            let double_quote = quote.to_string() + quote;
            text[1..text.len() - 1].replace(&double_quote, &quote)
        }

        let processed_value = if text.starts_with("U&'") && text.ends_with("'") {
            // Unicode single quoted string: U&'text'
            literal_text(&text[2..], "'")
        } else if text.starts_with("U&\"") && text.ends_with("\"") {
            // Unicode double quoted string: U&"text"
            literal_text(&text[2..], "\"")
        } else if text.len() >= 2 && text.starts_with("'") && text.ends_with("'") {
            // Single quoted string: 'text'
            literal_text(&text, "'")
        } else if text.len() >= 2 && text.starts_with("\"") && text.ends_with("\"") {
            // Double quoted string: "text"
            literal_text(&text, "\"")
        } else {
            return Err(ctx
                .error("malformed string literal")
                .at(value.as_ref())
                .emit());
        };

        Ok(Self {
            value: processed_value,
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct BinaryLiteral {
    pub value: Vec<u8>,
}

impl TryFromCst<&BinaryLiteralContext<'static>> for BinaryLiteral {
    fn try_from_cst_with_context(
        value: &BinaryLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let text = value.get_text();

        // Binary literals should start with x' and end with '
        if !text.starts_with("x'") || !text.ends_with("'") {
            return Err(ctx
                .error("binary literal must have format x'...'")
                .at(value)
                .emit());
        }

        // Extract hex string between the quotes
        let hex_str = &text[2..text.len() - 1]; // Skip "x'" prefix and "'" suffix
        let mut bytes = Vec::new();

        // Process pairs of hex digits
        for chunk in hex_str.as_bytes().chunks(2) {
            let hex_pair = std::str::from_utf8(chunk).map_err(|e| {
                ctx.error("invalid UTF-8 in binary literal")
                    .at(value)
                    .with_source_boxed(Box::new(e))
                    .emit()
            })?;
            let byte_val = u8::from_str_radix(hex_pair, 16).map_err(|e| {
                ctx.error("invalid hex digit in binary literal")
                    .at(value)
                    .with_source_boxed(Box::new(e))
                    .emit()
            })?;
            bytes.push(byte_val);
        }

        Ok(Self { value: bytes })
    }
}

#[derive(Debug, Clone, Default, PartialEq)]
pub struct NullLiteral;

impl TryFromCst<&NullLiteralContext<'static>> for NullLiteral {
    fn try_from_cst_with_context(
        _value: &NullLiteralContext<'static>,
        _ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        Ok(Self)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ArrayLiteral {
    pub elements: Vec<Rc<Expression>>,
}

impl FromCst<&ArrayLiteralContext<'static>> for ArrayLiteral {
    fn from_cst_with_context(value: &ArrayLiteralContext<'static>, ctx: &mut ParseContext) -> Self {
        let expressions = value.expression_all();
        let mut elements = Vec::new();

        for expression in expressions {
            let element = Expression::from_cst_with_context(expression, ctx);
            elements.push(Rc::new(element));
        }

        Self { elements }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct TupleLiteral {
    pub elements: Vec<Rc<Expression>>,
}

impl FromCst<&TupleLiteralContext<'static>> for TupleLiteral {
    fn from_cst_with_context(value: &TupleLiteralContext<'static>, ctx: &mut ParseContext) -> Self {
        let expressions = value.expression_all();
        let mut elements = Vec::new();

        for expression in expressions {
            let element = Expression::from_cst_with_context(expression, ctx);
            elements.push(Rc::new(element));
        }

        Self { elements }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct PairLiteral {
    pub left: Rc<Expression>,
    pub right: Rc<Expression>,
}

impl FromCst<&PairLiteralContext<'static>> for PairLiteral {
    fn from_cst_with_context(value: &PairLiteralContext<'static>, ctx: &mut ParseContext) -> Self {
        let left = if let Some(left_ctx) = value.left.as_ref() {
            Rc::new(Expression::from_cst_with_context(left_ctx.clone(), ctx))
        } else {
            let err = ctx
                .error("missing left expression in pair literal")
                .at(value)
                .emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        let right = if let Some(right_ctx) = value.right.as_ref() {
            Rc::new(Expression::from_cst_with_context(right_ctx.clone(), ctx))
        } else {
            let err = ctx
                .error("missing right expression in pair literal")
                .at(value)
                .emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        Self { left, right }
    }
}

#[derive(Debug, Clone, Default, PartialEq)]
pub struct StructLiteral {
    pub fields: Vec<(ParsedSimpleIdentifier, Rc<Expression>)>,
}

impl FromCst<&StructLiteralContext<'static>> for StructLiteral {
    fn from_cst_with_context(
        value: &StructLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Self {
        let identifiers = value.simpleIdentifier_all();
        let expressions = value.expression_all();

        if identifiers.len() != expressions.len() {
            ctx.error("struct literal has mismatched number of identifiers and expressions")
                .at(value)
                .emit();
            return Self::default();
        }

        let mut fields = Vec::with_capacity(identifiers.len());

        for (identifier, expression) in identifiers.into_iter().zip(expressions.into_iter()) {
            let field_name =
                ParsedSimpleIdentifier::from_cst_with_context(identifier.as_ref(), ctx);
            let field_expression = Expression::from_cst_with_context(expression, ctx);
            fields.push((field_name, Rc::new(field_expression)));
        }

        Self { fields }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ColumnReference {
    pub column_name: ParsedSimpleIdentifier,
}

impl From<&str> for ColumnReference {
    fn from(s: &str) -> Self {
        Self {
            column_name: SimpleIdentifier::new(s).into(),
        }
    }
}

impl From<String> for ColumnReference {
    fn from(s: String) -> Self {
        Self {
            column_name: SimpleIdentifier::new(s).into(),
        }
    }
}

impl From<SimpleIdentifier> for ColumnReference {
    fn from(id: SimpleIdentifier) -> Self {
        Self {
            column_name: id.into(),
        }
    }
}

impl TryFromCst<&ColumnReferenceAltContext<'static>> for ColumnReference {
    fn try_from_cst_with_context(
        value: &ColumnReferenceAltContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let col_ref = value.columnReference().ok_or_else(|| {
            ctx.error("missing identifier in column reference")
                .at(value)
                .emit()
        })?;

        let identifier_ctx = col_ref.simpleIdentifier().ok_or_else(|| {
            ctx.error("missing identifier in column reference")
                .at(value)
                .emit()
        })?;

        let column_name =
            ParsedSimpleIdentifier::from_cst_with_context(identifier_ctx.as_ref(), ctx);

        Ok(Self { column_name })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct UnaryPrefixOperator {
    pub operator: UnaryPrefixOp,
    pub operand: Rc<Expression>,
}

impl FromCst<&UnaryPrefixOperatorContext<'static>> for UnaryPrefixOperator {
    fn from_cst_with_context(
        value: &UnaryPrefixOperatorContext<'static>,
        ctx: &mut ParseContext,
    ) -> Self {
        let operator_token = value.operator.as_ref();

        let operator = if let Some(token) = operator_token {
            UnaryPrefixOp::from_cst(token)
        } else {
            None
        };

        let operator = match operator {
            Some(op) => op,
            None => {
                let err = ctx
                    .error("missing or unknown prefix operator")
                    .at(value)
                    .emit();
                return Self {
                    operator: UnaryPrefixOp::Not, // default fallback
                    operand: Rc::new(Expression::from_kind(ErrorExpression { error: err })),
                };
            }
        };

        let operand = if let Some(expr_ctx) = value.expression() {
            Rc::new(Expression::from_cst_with_context(expr_ctx, ctx))
        } else {
            let err = ctx.error("missing operand").at(value).emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        Self { operator, operand }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct UnaryPostfixOperator {
    pub operand: Rc<Expression>,
    pub operator: UnaryPostfixOp,
}

impl FromCst<&UnaryPostfixOperatorContext<'static>> for UnaryPostfixOperator {
    fn from_cst_with_context(
        value: &UnaryPostfixOperatorContext<'static>,
        ctx: &mut ParseContext,
    ) -> Self {
        let operator_token = value.operator.as_ref();

        let operator = if let Some(token) = operator_token {
            UnaryPostfixOp::from_cst(token)
        } else {
            None
        };

        let operator = match operator {
            Some(op) => op,
            None => {
                let err = ctx
                    .error("missing or unknown postfix operator")
                    .at(value)
                    .emit();
                return Self {
                    operand: Rc::new(Expression::from_kind(ErrorExpression {
                        error: err.clone(),
                    })),
                    operator: UnaryPostfixOp::Range, // default fallback
                };
            }
        };

        let operand = if let Some(expr_ctx) = value.expression() {
            Rc::new(Expression::from_cst_with_context(expr_ctx, ctx))
        } else {
            let err = ctx.error("missing operand").at(value).emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        Self { operand, operator }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct BinaryOperator {
    pub left: Rc<Expression>,
    pub operator: BinaryOp,
    pub right: Rc<Expression>,
}

impl FromCst<&BinaryOperatorContext<'static>> for BinaryOperator {
    fn from_cst_with_context(
        value: &BinaryOperatorContext<'static>,
        ctx: &mut ParseContext,
    ) -> Self {
        let operator_token = value.operator.as_ref();

        let operator = if let Some(token) = operator_token {
            BinaryOp::from_cst(token)
        } else {
            None
        };

        let operator = match operator {
            Some(op) => op,
            None => {
                let err = ctx
                    .error("missing or unknown binary operator")
                    .at(value)
                    .emit();
                return Self {
                    left: Rc::new(Expression::from_kind(ErrorExpression {
                        error: err.clone(),
                    })),
                    operator: BinaryOp::Add, // default fallback
                    right: Rc::new(Expression::from_kind(ErrorExpression { error: err })),
                };
            }
        };

        let left = if let Some(left_ctx) = value.left.clone() {
            Rc::new(Expression::from_cst_with_context(left_ctx, ctx))
        } else {
            let err = ctx.error("missing left operand").at(value).emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        let right = if let Some(right_ctx) = value.right.clone() {
            Rc::new(Expression::from_cst_with_context(right_ctx, ctx))
        } else {
            let err = ctx.error("missing right operand").at(value).emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        Self {
            left,
            operator,
            right,
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct FunctionCall {
    pub name: ParsedSimpleIdentifier,
    pub positional_args: Vec<Rc<Expression>>,
    pub named_args: VecMap<ParsedSimpleIdentifier, Rc<Expression>>,
}

impl TryFromCst<&FunctionCallContext<'static>> for FunctionCall {
    fn try_from_cst_with_context(
        value: &FunctionCallContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let name_ctx = value.simpleIdentifier();

        let name = if let Some(name_ctx) = name_ctx {
            ParsedSimpleIdentifier::from_cst_with_context(name_ctx.as_ref(), ctx)
        } else {
            return Err(ctx.error("missing function name").at(value).emit());
        };

        let mut positional_args = Vec::new();
        let mut named_args = VecMap::new();

        // Parse positional arguments
        for positional_arg in value.positionalArgument_all() {
            if let Some(expr_ctx) = positional_arg.expression() {
                let expression = Expression::from_cst_with_context(expr_ctx, ctx);
                positional_args.push(Rc::new(expression));
            }
        }

        // Parse named arguments
        for named_arg in value.namedArgument_all() {
            if let (Some(identifier_ctx), Some(expr_ctx)) =
                (named_arg.simpleIdentifier(), named_arg.expression())
            {
                let arg_name =
                    ParsedSimpleIdentifier::from_cst_with_context(identifier_ctx.as_ref(), ctx);

                let expression = Expression::from_cst_with_context(expr_ctx, ctx);
                named_args.insert(arg_name, Rc::new(expression));
            }
        }

        let ret = Self {
            name,
            positional_args,
            named_args,
        };

        Ok(ret)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct IndexAccess {
    pub value: Rc<Expression>,
    pub index: Rc<Expression>,
}

impl FromCst<&IndexAccessContext<'static>> for IndexAccess {
    fn from_cst_with_context(value: &IndexAccessContext<'static>, ctx: &mut ParseContext) -> Self {
        let expr_value = if let Some(value_ctx) = value.value.clone() {
            Rc::new(Expression::from_cst_with_context(value_ctx, ctx))
        } else {
            let err = ctx
                .error("missing value expression in index access")
                .at(value)
                .emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        let index_expr = if let Some(index_ctx) = value.index.clone() {
            Rc::new(Expression::from_cst_with_context(index_ctx, ctx))
        } else {
            let err = ctx
                .error("missing index expression in index access")
                .at(value)
                .emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        Self {
            value: expr_value,
            index: index_expr,
        }
    }
}

#[derive(Debug, Clone)]
pub struct FieldLookup {
    pub value: Rc<Expression>,
    pub field_identifier: ParsedSimpleIdentifier,
}

impl PartialEq for FieldLookup {
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value && self.field_identifier == other.field_identifier
    }
}

impl TryFromCst<&DerefContext<'static>> for FieldLookup {
    fn try_from_cst_with_context(
        value: &DerefContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let expr_value = if let Some(left_ctx) = value.left.clone() {
            Rc::new(Expression::from_cst_with_context(left_ctx, ctx))
        } else {
            return Err(ctx
                .error("missing left expression in field lookup")
                .at(value)
                .emit());
        };

        let field_identifier = if let Some(field_cst) = value.right.clone() {
            ParsedSimpleIdentifier::from_cst_with_context(field_cst.as_ref(), ctx)
        } else {
            return Err(ctx
                .error("missing field name in field lookup")
                .at(value)
                .emit());
        };

        let ret = Self {
            value: expr_value,
            field_identifier,
        };

        Ok(ret)
    }
}

#[derive(Debug, Clone)]
pub struct Cast {
    pub expression: Rc<Expression>,
    pub target_type: Rc<Type>,
}

impl PartialEq for Cast {
    fn eq(&self, other: &Self) -> bool {
        self.expression == other.expression && self.target_type == other.target_type
    }
}

impl FromCst<&CastContext<'static>> for Cast {
    fn from_cst_with_context(value: &CastContext<'static>, ctx: &mut ParseContext) -> Self {
        let expression = if let Some(left_ctx) = value.left.clone() {
            Rc::new(Expression::from_cst_with_context(left_ctx, ctx))
        } else {
            let err = ctx.error("missing expression in cast").at(value).emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        let target_type = if let Some(target_type_cst) = value.right.as_ref() {
            match Type::from_parse_tree(target_type_cst.as_ref()) {
                Ok(target_type) => Rc::new(target_type),
                Err(e) => {
                    let err = ctx
                        .error(&format!("failed to parse cast type: {}", e))
                        .at(value)
                        .emit();
                    ctx.add_error(err);
                    // Return a default type (e.g., String)
                    Rc::new(Type::String)
                }
            }
        } else {
            ctx.error("missing type in cast").at(value).emit();
            Rc::new(Type::String)
        };

        Self {
            expression,
            target_type,
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct IntervalLiteral {
    pub value: i64,
    pub unit: IntervalUnit,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum IntervalUnit {
    Nanosecond,
    Microsecond,
    Millisecond,
    Second,
    Minute,
    Hour,
    Day,
    Week,
    Month,
    Quarter,
    Year,
}

impl From<TruncUnit> for IntervalUnit {
    fn from(value: TruncUnit) -> Self {
        match value {
            TruncUnit::Second => IntervalUnit::Second,
            TruncUnit::Minute => IntervalUnit::Minute,
            TruncUnit::Hour => IntervalUnit::Hour,
            TruncUnit::Day => IntervalUnit::Day,
            TruncUnit::Week => IntervalUnit::Week,
            TruncUnit::Month => IntervalUnit::Month,
            TruncUnit::Quarter => IntervalUnit::Quarter,
            TruncUnit::Year => IntervalUnit::Year,
        }
    }
}

impl TryFromCst<&IntervalLiteralContext<'static>> for IntervalLiteral {
    fn try_from_cst_with_context(
        cst: &IntervalLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let unit = if cst.NANOSECOND_INTERVAL().is_some() {
            IntervalUnit::Nanosecond
        } else if cst.MICROSECOND_INTERVAL().is_some() {
            IntervalUnit::Microsecond
        } else if cst.MILLISECOND_INTERVAL().is_some() {
            IntervalUnit::Millisecond
        } else if cst.SECOND_INTERVAL().is_some() {
            IntervalUnit::Second
        } else if cst.MINUTE_INTERVAL().is_some() {
            IntervalUnit::Minute
        } else if cst.HOUR_INTERVAL().is_some() {
            IntervalUnit::Hour
        } else if cst.DAY_INTERVAL().is_some() {
            IntervalUnit::Day
        } else if cst.WEEK_INTERVAL().is_some() {
            IntervalUnit::Week
        } else if cst.MONTH_INTERVAL().is_some() {
            IntervalUnit::Month
        } else if cst.QUARTER_INTERVAL().is_some() {
            IntervalUnit::Quarter
        } else if cst.YEAR_INTERVAL().is_some() {
            IntervalUnit::Year
        } else {
            return Err(ctx.error("missing interval unit").at(cst).emit());
        };

        // Parse the numeric value from the context
        let value_text = cst.get_text();
        let value = value_text
            .chars()
            .take_while(|c| c.is_ascii_digit() || *c == '-')
            .collect::<String>()
            .parse::<i64>()
            .map_err(|e| {
                ctx.error("invalid interval value")
                    .at(cst)
                    .with_source_boxed(Box::new(e))
                    .emit()
            })?;

        Ok(Self { value, unit })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct TsTrunc {
    pub expression: Rc<Expression>,
    pub unit: TruncUnit,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TruncUnit {
    Second,
    Minute,
    Hour,
    Day,
    Week,
    Month,
    Quarter,
    Year,
}

impl FromCst<&TsTruncContext<'static>> for TsTrunc {
    fn from_cst_with_context(value: &TsTruncContext<'static>, ctx: &mut ParseContext) -> Self {
        let expression = if let Some(expr_ctx) = value.expression() {
            Rc::new(Expression::from_cst_with_context(expr_ctx, ctx))
        } else {
            let err = ctx.error("missing expression in ts_trunc").at(value).emit();
            Rc::new(Expression::from_kind(ErrorExpression { error: err }))
        };

        let unit = if value.SECOND_TRUNC().is_some() {
            TruncUnit::Second
        } else if value.MINUTE_TRUNC().is_some() {
            TruncUnit::Minute
        } else if value.HOUR_TRUNC().is_some() {
            TruncUnit::Hour
        } else if value.DAY_TRUNC().is_some() {
            TruncUnit::Day
        } else if value.WEEK_TRUNC().is_some() {
            TruncUnit::Week
        } else if value.MONTH_TRUNC().is_some() {
            TruncUnit::Month
        } else if value.QUARTER_TRUNC().is_some() {
            TruncUnit::Quarter
        } else if value.YEAR_TRUNC().is_some() {
            TruncUnit::Year
        } else {
            ctx.error("missing truncation unit").at(value).emit();
            TruncUnit::Second // default fallback
        };

        Self { expression, unit }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct RowsLiteral {
    pub value: i64,
}

impl TryFromCst<&RowsLiteralContext<'static>> for RowsLiteral {
    fn try_from_cst_with_context(
        value: &RowsLiteralContext<'static>,
        ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        let s = value.get_text();
        // Assuming format like "5 rows"
        let parsed_value = s
            .split_whitespace()
            .next()
            .ok_or_else(|| ctx.error("invalid rows literal").at(value).emit())?
            .parse::<i64>()
            .map_err(|e| {
                ctx.error("invalid rows literal value")
                    .at(value)
                    .with_source_boxed(Box::new(e))
                    .emit()
            })?;

        Ok(Self {
            value: parsed_value,
        })
    }
}

#[derive(Debug, Clone, Default, PartialEq)]
pub struct UnboundRangeLiteral;

impl TryFromCst<&UnboundRangeLiteralContext<'static>> for UnboundRangeLiteral {
    fn try_from_cst_with_context(
        _value: &UnboundRangeLiteralContext<'static>,
        _ctx: &mut ParseContext,
    ) -> Result<Self, Rc<TranslationError>> {
        Ok(Self)
    }
}

/// A lambda expression parameter with an optional type hint.
#[derive(Debug, Clone, PartialEq)]
pub struct LambdaParameter {
    pub name: SimpleIdentifier,
    pub type_hint: Option<Type>,
}

impl LambdaParameter {
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: SimpleIdentifier::new(&name.into()),
            type_hint: None,
        }
    }

    pub fn with_type(name: impl Into<String>, typ: Type) -> Self {
        Self {
            name: SimpleIdentifier::new(&name.into()),
            type_hint: Some(typ),
        }
    }
}

/// A lambda expression: `x -> x * 2` or `(x, y) -> x + y`
#[derive(Debug, Clone, PartialEq)]
pub struct Lambda {
    pub parameters: Vec<LambdaParameter>,
    pub body: Rc<Expression>,
}

/// Error that can occur when applying type hints to a lambda
#[derive(Error, Debug, Clone, PartialEq)]
pub enum LambdaTypeHintError {
    #[error("lambda has {expected} parameters but {got} type hints provided")]
    ArityMismatch { expected: usize, got: usize },

    #[error(
        "conflicting type hint for parameter '{param}': hint is {hint}, inferred is {inferred}"
    )]
    ConflictingHint {
        param: String,
        hint: Type,
        inferred: Type,
    },
}

impl Lambda {
    /// Create a new lambda with type hints applied to its parameters.
    ///
    /// If a parameter already has a type hint that conflicts with the provided type,
    /// returns an error. If the hints match or the parameter has no hint, the
    /// provided type is used.
    pub fn with_param_types(&self, param_types: &[Type]) -> Result<Lambda, LambdaTypeHintError> {
        if self.parameters.len() != param_types.len() {
            return Err(LambdaTypeHintError::ArityMismatch {
                expected: self.parameters.len(),
                got: param_types.len(),
            });
        }

        let mut new_params = Vec::with_capacity(self.parameters.len());
        for (param, typ) in self.parameters.iter().zip(param_types.iter()) {
            if let Some(ref hint) = param.type_hint {
                if hint != typ {
                    return Err(LambdaTypeHintError::ConflictingHint {
                        param: param.name.as_str().to_string(),
                        hint: hint.clone(),
                        inferred: typ.clone(),
                    });
                }
            }
            new_params.push(LambdaParameter {
                name: param.name.clone(),
                type_hint: Some(typ.clone()),
            });
        }

        Ok(Lambda {
            parameters: new_params,
            body: self.body.clone(),
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ErrorExpression {
    pub error: Rc<TranslationError>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use rstest::rstest;

    #[rstest]
    #[case("1d", IntervalUnit::Day, 1)]
    #[case("5h", IntervalUnit::Hour, 5)]
    #[case("30min", IntervalUnit::Minute, 30)]
    #[case("60s", IntervalUnit::Second, 60)]
    #[case("3mon", IntervalUnit::Month, 3)]
    #[case("2y", IntervalUnit::Year, 2)]
    fn test_interval_literal_parsing(
        #[case] input: &str,
        #[case] expected_unit: IntervalUnit,
        #[case] expected_value: i64,
    ) {
        let expr = Expression::parse(input);
        match expr.kind {
            ExpressionKind::IntervalLiteral(ref lit) => {
                assert_eq!(lit.value, expected_value, "Value mismatch for {}", input);

                let unit_matches = match (&lit.unit, &expected_unit) {
                    (IntervalUnit::Day, IntervalUnit::Day) => true,
                    (IntervalUnit::Hour, IntervalUnit::Hour) => true,
                    (IntervalUnit::Minute, IntervalUnit::Minute) => true,
                    (IntervalUnit::Second, IntervalUnit::Second) => true,
                    (IntervalUnit::Month, IntervalUnit::Month) => true,
                    (IntervalUnit::Year, IntervalUnit::Year) => true,
                    _ => false,
                };

                assert!(
                    unit_matches,
                    "Unit mismatch for {}: expected {:?}, got {:?}",
                    input, expected_unit, lit.unit
                );
            }
            _ => panic!(
                "Expected IntervalLiteral for input '{}', got {:?}",
                input, expr.kind
            ),
        }
    }
}

impl IntoTyped for Expression {
    type Output = TypedExpression;
    type Options = ExpressionTranslationOptions;

    fn typed_with(self) -> ExpressionTranslationOptions {
        ExpressionTranslationOptions::from_ast(Rc::new(self))
    }
}

impl IntoTyped for Rc<Expression> {
    type Output = TypedExpression;
    type Options = ExpressionTranslationOptions;

    fn typed_with(self) -> ExpressionTranslationOptions {
        ExpressionTranslationOptions::from_ast(self)
    }
}
