use super::ExprNode;

impl<'a> ExprNode<'a> {
    pub fn case(self) -> CaseNode<'a> {
        CaseNode {
            operand: Some(Box::new(self)),
        }
    }
}

pub fn case() -> CaseNode<'static> {
    CaseNode { operand: None }
}

#[derive(Clone, Debug)]
pub struct CaseNode<'a> {
    operand: Option<Box<ExprNode<'a>>>,
}

impl<'a> CaseNode<'a> {
    pub fn when_then<W: Into<ExprNode<'a>>, T: Into<ExprNode<'a>>>(
        self,
        when: W,
        then: T,
    ) -> WhenThenNode<'a> {
        WhenThenNode {
            prev_node: self,
            when_then: vec![(when.into(), then.into())],
        }
    }
}

#[derive(Clone, Debug)]
pub struct WhenThenNode<'a> {
    prev_node: CaseNode<'a>,
    when_then: Vec<(ExprNode<'a>, ExprNode<'a>)>,
}

impl<'a> WhenThenNode<'a> {
    pub fn when_then<W: Into<ExprNode<'a>>, T: Into<ExprNode<'a>>>(
        mut self,
        when: W,
        then: T,
    ) -> Self {
        self.when_then.push((when.into(), then.into()));
        self
    }

    pub fn or_else<T: Into<ExprNode<'a>>>(self, else_result: T) -> ExprNode<'a> {
        ExprNode::Case {
            operand: self.prev_node.operand,
            when_then: self.when_then,
            else_result: Some(Box::new(else_result.into())),
        }
    }

    pub fn end(self) -> ExprNode<'a> {
        ExprNode::Case {
            operand: self.prev_node.operand,
            when_then: self.when_then,
            else_result: None,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::ast_builder::{case, col, num, test_expr, text};

    #[test]
    fn case_with_operand() {
        let actual = col("id")
            .case()
            .when_then(num(1), text("a"))
            .when_then(2, text("b"))
            .or_else(text("c"));
        let expected = "
            CASE id
              WHEN 1 THEN 'a'
              WHEN 2 THEN 'b'
              ELSE 'c'
            END
            ";
        test_expr(actual, expected);

        let actual = col("id")
            .gt(10)
            .case()
            .when_then(true, text("a"))
            .when_then(false, text("b"))
            .end();
        let expected = "
            CASE id > 10
              WHEN True THEN 'a'
              WHEN False THEN 'b'
            END
            ";
        test_expr(actual, expected);
    }

    #[test]
    fn case_without_operand() {
        let actual = case()
            .when_then(
                "City IS NULL",
                case()
                    .when_then("Country IS NULL", text("weird"))
                    .or_else("Country"),
            )
            .or_else("City");
        let expected = r"
            CASE
              WHEN City IS NULL THEN CASE WHEN Country IS NULL THEN 'weird'
                                          ELSE Country
                                          END
              ELSE City
            END
            ";
        test_expr(actual, expected);
    }
}
