use crate::err::Context;
use crate::tree::ast::expression::{Cast, Expression, FieldReference, TruncUnit, TsTrunc};
use crate::tree::ast::identifier::SimpleIdentifier;
use crate::tree::ast::node::Span;
use crate::tree::builder::{ExpressionBuilder, IntoExpressionBuilder};
use crate::types::Type;
use std::sync::Arc;

/// Builder for cast expressions (e.g., value::type)
#[derive(Debug)]
pub struct CastBuilder {
    expression: Box<dyn ExpressionBuilder>,
    target_type: Type,
}

impl CastBuilder {
    pub fn new(expression: impl IntoExpressionBuilder, target_type: Type) -> Self {
        Self {
            expression: expression.into_expression_builder(),
            target_type,
        }
    }
}

impl ExpressionBuilder for CastBuilder {
    fn build(&self) -> Expression {
        Expression {
            span: Span::NONE,
            kind: Cast {
                expression: Arc::new(self.expression.build()),
                target_type: Arc::new(self.target_type.clone()),
            }
            .into(),
        }
    }
}

impl IntoExpressionBuilder for CastBuilder {
    fn into_expression_builder(self) -> Box<dyn ExpressionBuilder> {
        Box::new(self)
    }
}

/// Builder for timestamp truncation expressions (e.g., ts_trunc(timestamp, 'hour'))
#[derive(Debug)]
pub struct TsTruncBuilder {
    expression: Box<dyn ExpressionBuilder>,
    unit: TruncUnit,
    multiplier: u32,
}

impl TsTruncBuilder {
    pub fn new(expression: impl IntoExpressionBuilder, unit: TruncUnit) -> Self {
        Self {
            expression: expression.into_expression_builder(),
            unit,
            multiplier: 1,
        }
    }

    pub fn with_multiplier(
        expression: impl IntoExpressionBuilder,
        unit: TruncUnit,
        multiplier: u32,
    ) -> Self {
        Self {
            expression: expression.into_expression_builder(),
            unit,
            multiplier,
        }
    }

    /// Create a ts_trunc builder that truncates to seconds
    pub fn to_second(expression: impl IntoExpressionBuilder) -> Self {
        Self::new(expression, TruncUnit::Second)
    }

    /// Create a ts_trunc builder that truncates to minutes
    pub fn to_minute(expression: impl IntoExpressionBuilder) -> Self {
        Self::new(expression, TruncUnit::Minute)
    }

    /// Create a ts_trunc builder that truncates to hours
    pub fn to_hour(expression: impl IntoExpressionBuilder) -> Self {
        Self::new(expression, TruncUnit::Hour)
    }

    /// Create a ts_trunc builder that truncates to days
    pub fn to_day(expression: impl IntoExpressionBuilder) -> Self {
        Self::new(expression, TruncUnit::Day)
    }

    /// Create a ts_trunc builder that truncates to weeks
    pub fn to_week(expression: impl IntoExpressionBuilder) -> Self {
        Self::new(expression, TruncUnit::Week)
    }

    /// Create a ts_trunc builder that truncates to months
    pub fn to_month(expression: impl IntoExpressionBuilder) -> Self {
        Self::new(expression, TruncUnit::Month)
    }

    /// Create a ts_trunc builder that truncates to years
    pub fn to_year(expression: impl IntoExpressionBuilder) -> Self {
        Self::new(expression, TruncUnit::Year)
    }
}

impl ExpressionBuilder for TsTruncBuilder {
    fn build(&self) -> Expression {
        Expression {
            span: Span::NONE,
            kind: TsTrunc {
                expression: Arc::new(self.expression.build()),
                unit: self.unit.clone(),
                multiplier: self.multiplier,
            }
            .into(),
        }
    }
}

impl IntoExpressionBuilder for TsTruncBuilder {
    fn into_expression_builder(self) -> Box<dyn ExpressionBuilder> {
        Box::new(self)
    }
}

/// Builder for field reference expressions
#[derive(Debug)]
pub struct FieldReferenceBuilder {
    field_name: SimpleIdentifier,
}

impl FieldReferenceBuilder {
    pub fn new(name: impl Into<SimpleIdentifier>) -> Self {
        Self {
            field_name: name.into(),
        }
    }
}

impl ExpressionBuilder for FieldReferenceBuilder {
    fn build(&self) -> Expression {
        Expression {
            span: Span::NONE,
            kind: FieldReference {
                field_name: self.field_name.clone().into(),
            }
            .into(),
        }
    }
}

impl IntoExpressionBuilder for FieldReferenceBuilder {
    fn into_expression_builder(self) -> Box<dyn ExpressionBuilder> {
        Box::new(self)
    }
}

/// Convenience function to create a field reference
pub fn field_ref(name: impl Into<SimpleIdentifier>) -> FieldReferenceBuilder {
    FieldReferenceBuilder::new(name)
}

/// Builder for error expressions (for testing error handling)
#[derive(Debug)]
pub struct ErrorBuilder {
    message: String,
}

impl ErrorBuilder {
    pub fn new(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
        }
    }
}

impl ExpressionBuilder for ErrorBuilder {
    fn build(&self) -> Expression {
        use crate::err::TranslationError;
        use crate::tree::ast::expression::ErrorExpression;

        Expression {
            span: Span::NONE,
            kind: ErrorExpression {
                error: Arc::new(TranslationError::new(Context::new(0..=0, &self.message))),
            }
            .into(),
        }
    }
}

impl IntoExpressionBuilder for ErrorBuilder {
    fn into_expression_builder(self) -> Box<dyn ExpressionBuilder> {
        Box::new(self)
    }
}

// Convenience functions

/// Create a cast expression
pub fn cast(expression: impl IntoExpressionBuilder, target_type: Type) -> CastBuilder {
    CastBuilder::new(expression, target_type)
}

/// Create a ts_trunc expression
pub fn at(expression: impl IntoExpressionBuilder, unit: TruncUnit) -> TsTruncBuilder {
    TsTruncBuilder::new(expression, unit)
}

/// Create a ts_trunc expression that truncates to seconds
pub fn at_second(expression: impl IntoExpressionBuilder) -> TsTruncBuilder {
    TsTruncBuilder::to_second(expression)
}

/// Create a ts_trunc expression that truncates to minutes
pub fn at_minute(expression: impl IntoExpressionBuilder) -> TsTruncBuilder {
    TsTruncBuilder::to_minute(expression)
}

/// Create a ts_trunc expression that truncates to hours
pub fn at_hour(expression: impl IntoExpressionBuilder) -> TsTruncBuilder {
    TsTruncBuilder::to_hour(expression)
}

/// Create a ts_trunc expression that truncates to days
pub fn at_day(expression: impl IntoExpressionBuilder) -> TsTruncBuilder {
    TsTruncBuilder::to_day(expression)
}

/// Create a ts_trunc expression that truncates to weeks
pub fn at_week(expression: impl IntoExpressionBuilder) -> TsTruncBuilder {
    TsTruncBuilder::to_week(expression)
}

/// Create a ts_trunc expression that truncates to months
pub fn at_month(expression: impl IntoExpressionBuilder) -> TsTruncBuilder {
    TsTruncBuilder::to_month(expression)
}

/// Create a ts_trunc expression that truncates to years
pub fn at_year(expression: impl IntoExpressionBuilder) -> TsTruncBuilder {
    TsTruncBuilder::to_year(expression)
}

/// Create an error expression (for testing)
pub fn error(message: impl Into<String>) -> ErrorBuilder {
    ErrorBuilder::new(message)
}
