use crate::sql::expression::identifier::{Identifier, SimpleIdentifier};
use crate::sql::expression::SQLExpression;
use derive_more::From;
use std::fmt;
use std::fmt::{Display, Formatter};

#[derive(Debug, Clone, From, PartialEq, Eq)]
pub enum Projection {
    Binding(Binding),
    ColumnProjection(ColumnProjection),
}

impl Projection {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        match self {
            Projection::Binding(binding) => binding.fmt_indented(f, indentation),
            Projection::ColumnProjection(column_projection) => {
                column_projection.fmt_indented(f, indentation)
            }
        }
    }
}

impl Display for Projection {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Binding {
    pub name: SimpleIdentifier,
    pub expression: SQLExpression,
}

impl Binding {
    pub fn new(name: SimpleIdentifier, expression: SQLExpression) -> Self {
        Self { name, expression }
    }

    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        self.expression.fmt_indented(f, indentation)?;
        write!(f, " AS ")?;
        self.name.fmt_indented(f, indentation)
    }
}

impl Display for Binding {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ColumnProjection {
    pub identifier: Identifier,
}

impl ColumnProjection {
    pub fn new(column_reference: Identifier) -> Self {
        Self {
            identifier: column_reference,
        }
    }
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        self.identifier.fmt_indented(f, indentation)
    }
}

impl Display for ColumnProjection {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}
