use crate::tree::ast::expression::{
    ArrayLiteral, Expression, PairLiteral, StructLiteral, TupleLiteral,
};
use crate::tree::ast::identifier::SimpleIdentifier;
use crate::tree::ast::node::Span;
use crate::tree::builder::{ExpressionBuilder, IntoExpressionBuilder};
use std::rc::Rc;

/// Builder for array literal expressions
#[derive(Debug)]
pub struct ArrayLiteralBuilder {
    elements: Vec<Box<dyn ExpressionBuilder>>,
}

impl ArrayLiteralBuilder {
    pub fn new() -> Self {
        Self {
            elements: Vec::new(),
        }
    }

    pub fn element(mut self, elem: impl IntoExpressionBuilder) -> Self {
        self.elements.push(elem.into_expression_builder());
        self
    }
}

impl ExpressionBuilder for ArrayLiteralBuilder {
    fn build(&self) -> Expression {
        let elements = self.elements.iter().map(|b| Rc::new(b.build())).collect();

        Expression {
            span: Span::NONE,
            kind: ArrayLiteral { elements }.into(),
        }
    }
}

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

/// Builder for tuple literal expressions
#[derive(Debug)]
pub struct TupleLiteralBuilder {
    elements: Vec<Box<dyn ExpressionBuilder>>,
}

impl TupleLiteralBuilder {
    pub fn new() -> Self {
        Self {
            elements: Vec::new(),
        }
    }

    pub fn element(mut self, elem: impl IntoExpressionBuilder) -> Self {
        self.elements.push(elem.into_expression_builder());
        self
    }
}

impl ExpressionBuilder for TupleLiteralBuilder {
    fn build(&self) -> Expression {
        let elements = self.elements.iter().map(|b| Rc::new(b.build())).collect();

        Expression {
            span: Span::NONE,
            kind: TupleLiteral { elements }.into(),
        }
    }
}

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

/// Builder for struct literal expressions
#[derive(Debug)]
pub struct StructLiteralBuilder {
    fields: Vec<(String, Box<dyn ExpressionBuilder>)>,
}

impl StructLiteralBuilder {
    pub fn new() -> Self {
        Self { fields: Vec::new() }
    }

    pub fn field(mut self, name: impl Into<String>, value: impl IntoExpressionBuilder) -> Self {
        self.fields
            .push((name.into(), value.into_expression_builder()));
        self
    }
}

impl ExpressionBuilder for StructLiteralBuilder {
    fn build(&self) -> Expression {
        let mut fields = Vec::with_capacity(self.fields.len());
        for (name, value) in &self.fields {
            fields.push((SimpleIdentifier::new(name).into(), Rc::new(value.build())));
        }

        Expression {
            span: Span::NONE,
            kind: StructLiteral { fields }.into(),
        }
    }
}

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

/// Builder for pair literal expressions
#[derive(Debug)]
pub struct PairLiteralBuilder {
    left: Box<dyn ExpressionBuilder>,
    right: Box<dyn ExpressionBuilder>,
}

impl PairLiteralBuilder {
    pub fn new(left: impl IntoExpressionBuilder, right: impl IntoExpressionBuilder) -> Self {
        Self {
            left: left.into_expression_builder(),
            right: right.into_expression_builder(),
        }
    }
}

impl ExpressionBuilder for PairLiteralBuilder {
    fn build(&self) -> Expression {
        Expression {
            span: Span::NONE,
            kind: PairLiteral {
                left: Rc::new(self.left.build()),
                right: Rc::new(self.right.build()),
            }
            .into(),
        }
    }
}

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

// Convenience functions
pub fn array() -> ArrayLiteralBuilder {
    ArrayLiteralBuilder::new()
}

pub fn tuple() -> TupleLiteralBuilder {
    TupleLiteralBuilder::new()
}

pub fn pair(
    left: impl IntoExpressionBuilder,
    right: impl IntoExpressionBuilder,
) -> PairLiteralBuilder {
    PairLiteralBuilder::new(left, right)
}

pub fn struct_literal() -> StructLiteralBuilder {
    StructLiteralBuilder::new()
}
