use crate::data::{Value, condition::Condition, executable_query::ExecutableQuery};

pub struct QueryBuilder<'a> {
  table: &'a str,
  columns: Vec<&'a str>,
  distinct_on: Option<Vec<String>>,
  condition: Option<Condition>,
  order_by: Vec<String>,
  limit: Option<usize>,
  offset: Option<usize>,
}

impl<'a> QueryBuilder<'a> {
  pub fn new(table: &'a str) -> Self {
    QueryBuilder {
      table,
      columns: Vec::new(),
      distinct_on: None,
      condition: None,
      order_by: Vec::new(),
      limit: None,
      offset: None,
    }
  }

  pub fn select(mut self, columns: &[&'a str]) -> Self {
    self.columns.extend(columns);
    self
  }

  pub fn distinct_on(mut self, columns: &[&str]) -> Self {
    self.distinct_on = Some(columns.iter().map(|s| s.to_string()).collect());
    self
  }

  pub fn where_condition(mut self, condition: Condition) -> Self {
    self.condition = Some(condition);
    self
  }

  pub fn order_by(mut self, column: &str, direction: Option<OrderDirection>) -> Self {
    match direction {
      Some(order) => self.order_by.push(format!("{} {}", column, order)),
      None => self.order_by.push(column.to_string()),
    }
    self
  }

  pub fn limit(mut self, limit: usize) -> Self {
    self.limit = Some(limit);
    self
  }
}

pub enum OrderDirection {
  Asc,
  Desc,
}

impl std::fmt::Display for OrderDirection {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    let direction_str = match self {
      OrderDirection::Asc => "ASC",
      OrderDirection::Desc => "DESC",
    };
    write!(f, "{}", direction_str)
  }
}

impl<'a> ExecutableQuery for QueryBuilder<'a> {
  fn build(&self) -> (String, Vec<Value>) {
    let columns = if self.columns.is_empty() {
      "*".to_string()
    } else {
      self.columns.join(",")
    };

    let mut query = if let Some(distinct_columns) = &self.distinct_on {
      let distinct_on_clause = distinct_columns.join(",");
      format!("SELECT DISTINCT ON ({}) {}", distinct_on_clause, columns)
    } else {
      format!("SELECT {}", columns)
    };

    query.push_str(format!(" FROM {}", self.table).as_str());

    let mut values: Vec<Value> = Vec::new();
    if let Some(condition) = &self.condition {
      let where_condition = condition.to_sql(&mut 0);
      query.push_str(&format!(" WHERE {}", where_condition.0));
      values = where_condition.1;
    }

    if !self.order_by.is_empty() {
      query.push_str(&format!(" ORDER BY {}", self.order_by.join(" ORDER BY")));
    }

    if let Some(limit) = self.limit {
      query.push_str(&format!(" LIMIT {}", limit));
    }

    if let Some(offset) = self.offset {
      query.push_str(&format!(" OFFSET {}", offset));
    }

    (query, values)
  }
}
