use crate::sql::expression::identifier::SimpleIdentifier;
use crate::sql::query::cte::CTE;
use crate::sql::query::{SQLQuery, SQLQueryExpression};
use crate::write_utils::pad;
use std::fmt;
use std::fmt::{Display, Formatter};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type {
    Union,
    UnionAll,
    UnionDistinct,
    Intersect,
    IntersectAll,
    IntersectDistinct,
    Except,
    ExceptAll,
    ExceptDistinct,
}

impl Type {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, _indentation: usize) -> fmt::Result {
        match self {
            Type::Union => write!(f, "UNION"),
            Type::UnionAll => write!(f, "UNION ALL"),
            Type::UnionDistinct => write!(f, "UNION DISTINCT"),
            Type::Intersect => write!(f, "INTERSECT"),
            Type::IntersectAll => write!(f, "INTERSECT ALL"),
            Type::IntersectDistinct => write!(f, "INTERSECT DISTINCT"),
            Type::Except => write!(f, "EXCEPT"),
            Type::ExceptAll => write!(f, "EXCEPT ALL"),
            Type::ExceptDistinct => write!(f, "EXCEPT DISTINCT"),
        }
    }
}

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

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SetOperation {
    pub typ: Type,
    pub left: Box<SQLQueryExpression>,
    pub right: Box<SQLQueryExpression>,
    pub cte: CTE,
}

impl SetOperation {
    pub fn new(left: SQLQueryExpression, right: SQLQueryExpression) -> Self {
        Self {
            typ: Type::UnionAll,
            left: Box::new(left),
            right: Box::new(right),
            cte: CTE::default(),
        }
    }

    pub fn with_cte(mut self, name: SimpleIdentifier, query: SQLQuery) -> Self {
        self.cte = self.cte.with(name, query);
        self
    }

    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        self.cte.fmt_indented(f, indentation)?;

        writeln!(f)?;
        pad(f, indentation)?;
        self.left.fmt_indented(f, indentation)?;
        writeln!(f)?;
        writeln!(f)?;
        pad(f, indentation)?;
        self.typ.fmt_indented(f, indentation)?;
        writeln!(f)?;
        writeln!(f)?;
        pad(f, indentation)?;
        self.right.fmt_indented(f, indentation)
    }
}
