use crate::error::{DatabaseError, DatabaseResult, ErrorContext};
use sqlx::postgres::{PgArguments, PgRow};
use sqlx::{Arguments, Executor, FromRow, Postgres};
use std::fmt;

/// Type-safe query builder for PostgreSQL
///
/// Provides a fluent interface for building SQL queries with parameter binding
/// and type safety. Supports SELECT, INSERT, UPDATE, and DELETE operations.
#[derive(Debug, Clone)]
pub struct QueryBuilder {
    /// The SQL query string being built
    query: String,
    /// Bound arguments for the query
    args: PgArguments,
}

impl QueryBuilder {
    /// Create a new empty query builder
    ///
    /// # Returns
    ///
    /// A new `QueryBuilder` instance ready for query construction
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let builder = QueryBuilder::new();
    /// ```
    pub fn new() -> Self {
        Self {
            query: String::new(),
            args: PgArguments::default(),
        }
    }

    /// Create a SELECT query builder
    ///
    /// # Arguments
    ///
    /// * `columns` - Array of column names to select. If empty, selects all columns (*)
    ///
    /// # Returns
    ///
    /// A `SelectBuilder` for constructing the SELECT query
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["id", "name"])
    ///     .from("users")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn select<T: AsRef<str>>(columns: &[T]) -> SelectBuilder {
        let columns_str = if columns.is_empty() {
            "*".to_string()
        } else {
            columns
                .iter()
                .map(|c| c.as_ref())
                .collect::<Vec<_>>()
                .join(", ")
        };

        SelectBuilder {
            query: QueryBuilder::new(),
            columns: columns_str,
            from_clause: None,
            where_conditions: Vec::new(),
            order_by: Vec::new(),
            group_by: Vec::new(),
            having_conditions: Vec::new(),
            limit_clause: None,
            offset_clause: None,
            joins: Vec::new(),
        }
    }

    /// Create an INSERT query builder
    ///
    /// # Arguments
    ///
    /// * `table` - The table name to insert into
    ///
    /// # Returns
    ///
    /// An `InsertBuilder` for constructing the INSERT query
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::insert("users")
    ///     .values(&[("name", "John"), ("email", "john@example.com")])
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn insert(table: &str) -> InsertBuilder {
        InsertBuilder {
            query: QueryBuilder::new(),
            table: table.to_string(),
            columns: Vec::new(),
            values: Vec::new(),
            on_conflict: None,
            returning: None,
        }
    }

    /// Create an UPDATE query builder
    ///
    /// # Arguments
    ///
    /// * `table` - The table name to update
    ///
    /// # Returns
    ///
    /// An `UpdateBuilder` for constructing the UPDATE query
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::update("users")
    ///     .set("name", "Jane")
    ///     .where_eq("id", 1)
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn update(table: &str) -> UpdateBuilder {
        UpdateBuilder {
            query: QueryBuilder::new(),
            table: table.to_string(),
            set_clauses: Vec::new(),
            where_conditions: Vec::new(),
            returning: None,
        }
    }

    /// Create a DELETE query builder
    ///
    /// # Arguments
    ///
    /// * `table` - The table name to delete from
    ///
    /// # Returns
    ///
    /// A `DeleteBuilder` for constructing the DELETE query
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::delete("users")
    ///     .where_eq("id", 1)
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn delete(table: &str) -> DeleteBuilder {
        DeleteBuilder {
            query: QueryBuilder::new(),
            table: table.to_string(),
            where_conditions: Vec::new(),
            returning: None,
        }
    }

    /// Add a parameter to the query
    ///
    /// Binds a value as a parameter to the query, using PostgreSQL's
    /// parameter placeholder system ($1, $2, etc.).
    ///
    /// # Arguments
    ///
    /// * `value` - The value to bind as a parameter
    ///
    /// # Returns
    ///
    /// A mutable reference to self for method chaining
    ///
    /// # Type Parameters
    ///
    /// * `T` - Must implement SQLx encoding and type traits for PostgreSQL
    pub fn bind<T>(&mut self, value: T) -> &mut Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        let _ = self.args.add(value);
        self
    }

    /// Get the generated SQL query string
    ///
    /// # Returns
    ///
    /// The SQL query string that has been built
    pub fn sql(&self) -> &str {
        &self.query
    }

    /// Get the bound arguments for the query
    ///
    /// # Returns
    ///
    /// A reference to the PostgreSQL arguments that have been bound
    pub fn arguments(&self) -> &PgArguments {
        &self.args
    }

    /// Execute the query and return the number of affected rows
    ///
    /// # Arguments
    ///
    /// * `executor` - Database executor (pool, connection, or transaction)
    ///
    /// # Returns
    ///
    /// The number of rows affected by the query
    ///
    /// # Errors
    ///
    /// Returns `DatabaseError` if the query execution fails
    pub async fn execute<'e, E>(&self, executor: E) -> DatabaseResult<u64>
    where
        E: Executor<'e, Database = Postgres>,
    {
        let result = sqlx::query_with(&self.query, self.args.clone())
            .execute(executor)
            .await
            .with_query_context(&self.query)?;

        Ok(result.rows_affected())
    }

    /// Execute the query and fetch all matching rows
    ///
    /// # Arguments
    ///
    /// * `executor` - Database executor (pool, connection, or transaction)
    ///
    /// # Returns
    ///
    /// A vector of all rows returned by the query
    ///
    /// # Errors
    ///
    /// Returns `DatabaseError` if the query execution fails
    ///
    /// # Type Parameters
    ///
    /// * `T` - The type to deserialize rows into (must implement `FromRow`)
    pub async fn fetch_all<'e, E, T>(&self, executor: E) -> DatabaseResult<Vec<T>>
    where
        E: Executor<'e, Database = Postgres>,
        T: for<'r> FromRow<'r, PgRow> + Send + Unpin,
    {
        let rows = sqlx::query_as_with(&self.query, self.args.clone())
            .fetch_all(executor)
            .await
            .with_query_context(&self.query)?;

        Ok(rows)
    }

    /// Execute the query and fetch exactly one row
    ///
    /// # Arguments
    ///
    /// * `executor` - Database executor (pool, connection, or transaction)
    ///
    /// # Returns
    ///
    /// The single row returned by the query
    ///
    /// # Errors
    ///
    /// Returns `DatabaseError` if:
    /// - The query execution fails
    ///
    /// - No rows are found
    /// - More than one row is found
    ///
    /// # Type Parameters
    ///
    /// * `T` - The type to deserialize the row into (must implement `FromRow`)
    pub async fn fetch_one<'e, E, T>(&self, executor: E) -> DatabaseResult<T>
    where
        E: Executor<'e, Database = Postgres>,
        T: for<'r> FromRow<'r, PgRow> + Send + Unpin,
    {
        let row = sqlx::query_as_with(&self.query, self.args.clone())
            .fetch_one(executor)
            .await
            .with_query_context(&self.query)?;

        Ok(row)
    }

    /// Execute the query and fetch an optional row
    ///
    /// # Arguments
    ///
    /// * `executor` - Database executor (pool, connection, or transaction)
    ///
    /// # Returns
    ///
    /// `Some(row)` if a row is found, `None` if no rows match
    ///
    /// # Errors
    ///
    /// Returns `DatabaseError` if:
    /// - The query execution fails
    ///
    /// - More than one row is found
    ///
    /// # Type Parameters
    ///
    /// * `T` - The type to deserialize the row into (must implement `FromRow`)
    pub async fn fetch_optional<'e, E, T>(&self, executor: E) -> DatabaseResult<Option<T>>
    where
        E: Executor<'e, Database = Postgres>,
        T: for<'r> FromRow<'r, PgRow> + Send + Unpin,
    {
        let row = sqlx::query_as_with(&self.query, self.args.clone())
            .fetch_optional(executor)
            .await
            .with_query_context(&self.query)?;

        Ok(row)
    }
}

impl Default for QueryBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// SELECT query builder
///
/// Provides a fluent interface for building SELECT queries with support for
///
/// WHERE conditions, JOINs, ORDER BY, GROUP BY, LIMIT, and OFFSET clauses.
#[derive(Debug, Clone)]
pub struct SelectBuilder {
    /// The underlying query builder
    query: QueryBuilder,
    /// Comma-separated column names to select
    columns: String,
    /// FROM table clause
    from_clause: Option<String>,
    /// WHERE conditions that will be joined with AND
    where_conditions: Vec<String>,
    /// ORDER BY clauses
    order_by: Vec<String>,
    /// GROUP BY columns
    group_by: Vec<String>,
    /// HAVING conditions for grouped queries
    having_conditions: Vec<String>,
    /// LIMIT clause for result count
    limit_clause: Option<u64>,
    /// OFFSET clause for result pagination
    offset_clause: Option<u64>,
    /// JOIN clauses (INNER, LEFT, etc.)
    joins: Vec<String>,
}

impl SelectBuilder {
    /// Add FROM clause to specify the source table
    ///
    /// # Arguments
    ///
    /// * `table` - The table name to select from
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["*"])
    ///     .from("users")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn from(mut self, table: &str) -> Self {
        self.from_clause = Some(table.to_string());
        self
    }

    /// Add WHERE equality condition
    ///
    /// # Arguments
    ///
    /// * `column` - The column name to filter on
    /// * `value` - The value to compare against
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Type Parameters
    ///
    /// * `T` - Must implement SQLx encoding and type traits for PostgreSQL
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["*"])
    ///     .from("users")
    ///     .where_eq("active", true)
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn where_eq<T>(mut self, column: &str, value: T) -> Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        let placeholder = format!("${}", self.query.args.len() + 1);
        self.where_conditions
            .push(format!("{} = {}", column, placeholder));
        self.query.bind(value);
        self
    }

    /// Add WHERE IN condition for multiple values
    ///
    /// # Arguments
    ///
    /// * `column` - The column name to filter on
    /// * `values` - Vector of values to match against
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Type Parameters
    ///
    /// * `T` - Must implement SQLx encoding and type traits for PostgreSQL
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["*"])
    ///     .from("users")
    ///     .where_in("status", vec!["active", "pending"])
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn where_in<T>(mut self, column: &str, values: Vec<T>) -> Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        if values.is_empty() {
            return self;
        }

        let placeholders: Vec<String> = values
            .into_iter()
            .map(|value| {
                let placeholder = format!("${}", self.query.args.len() + 1);
                self.query.bind(value);
                placeholder
            })
            .collect();

        self.where_conditions
            .push(format!("{} IN ({})", column, placeholders.join(", ")));
        self
    }

    /// Add custom WHERE condition with raw SQL
    ///
    /// # Arguments
    ///
    /// * `condition` - Raw SQL condition string
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Safety
    ///
    /// This method allows raw SQL which could be vulnerable to SQL injection.
    ///
    /// Only use with trusted input or properly escaped values.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["*"])
    ///     .from("users")
    ///     .where_raw("created_at > NOW() - INTERVAL '1 day'")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn where_raw(mut self, condition: &str) -> Self {
        self.where_conditions.push(condition.to_string());
        self
    }

    /// Add INNER JOIN clause
    ///
    /// # Arguments
    ///
    /// * `table` - The table to join with
    /// * `on` - The join condition
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["u.name", "p.title"])
    ///     .from("users u")
    ///     .inner_join("posts p", "u.id = p.user_id")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn inner_join(mut self, table: &str, on: &str) -> Self {
        self.joins.push(format!("INNER JOIN {} ON {}", table, on));
        self
    }

    /// Add LEFT JOIN clause
    ///
    /// # Arguments
    ///
    /// * `table` - The table to join with
    /// * `on` - The join condition
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["u.name", "p.title"])
    ///     .from("users u")
    ///     .left_join("posts p", "u.id = p.user_id")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn left_join(mut self, table: &str, on: &str) -> Self {
        self.joins.push(format!("LEFT JOIN {} ON {}", table, on));
        self
    }

    /// Add ORDER BY clause for result sorting
    ///
    /// # Arguments
    ///
    /// * `column` - The column name to sort by
    /// * `direction` - Sort direction (ASC or DESC)
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::{QueryBuilder, OrderDirection};
    ///
    /// let query = QueryBuilder::select(&["*"])
    ///     .from("users")
    ///     .order_by("created_at", OrderDirection::Desc)
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn order_by(mut self, column: &str, direction: OrderDirection) -> Self {
        self.order_by.push(format!("{} {}", column, direction));
        self
    }

    /// Add GROUP BY clause for result grouping
    ///
    /// # Arguments
    ///
    /// * `column` - The column name to group by
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["status", "COUNT(*)"])
    ///     .from("users")
    ///     .group_by("status")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn group_by(mut self, column: &str) -> Self {
        self.group_by.push(column.to_string());
        self
    }

    /// Add HAVING condition for grouped results
    ///
    /// # Arguments
    ///
    /// * `condition` - The HAVING condition
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["status", "COUNT(*) as count"])
    ///     .from("users")
    ///     .group_by("status")
    ///     .having("COUNT(*) > 10")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn having(mut self, condition: &str) -> Self {
        self.having_conditions.push(condition.to_string());
        self
    }

    /// Add LIMIT clause to restrict number of results
    ///
    /// # Arguments
    ///
    /// * `count` - Maximum number of rows to return
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["*"])
    ///     .from("users")
    ///     .limit(10)
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn limit(mut self, count: u64) -> Self {
        self.limit_clause = Some(count);
        self
    }

    /// Add OFFSET clause for result pagination
    ///
    /// # Arguments
    ///
    /// * `count` - Number of rows to skip
    ///
    /// # Returns
    ///
    /// Self for method chaining
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["*"])
    ///     .from("users")
    ///     .limit(10)
    ///     .offset(20)
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn offset(mut self, count: u64) -> Self {
        self.offset_clause = Some(count);
        self
    }

    /// Build the final SELECT query
    ///
    /// Constructs the complete SQL query string from all the added clauses.
    ///
    /// # Returns
    ///
    /// A `QueryBuilder` ready for execution
    ///
    /// # Errors
    ///
    /// Returns `DatabaseError::Query` if the query is incomplete (missing FROM clause)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use database::QueryBuilder;
    ///
    /// let query = QueryBuilder::select(&["id", "name"])
    ///     .from("users")
    ///     .where_eq("active", true)
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn build(mut self) -> DatabaseResult<QueryBuilder> {
        let mut query_parts = vec![format!("SELECT {}", self.columns)];

        if let Some(from) = &self.from_clause {
            query_parts.push(format!("FROM {}", from));
        } else {
            return Err(DatabaseError::Query {
                query: "incomplete".to_string(),
                message: "SELECT query must have FROM clause".to_string(),
            });
        }

        // Add JOINs
        query_parts.extend(self.joins);

        // Add WHERE conditions
        if !self.where_conditions.is_empty() {
            query_parts.push(format!("WHERE {}", self.where_conditions.join(" AND ")));
        }

        // Add GROUP BY
        if !self.group_by.is_empty() {
            query_parts.push(format!("GROUP BY {}", self.group_by.join(", ")));
        }

        // Add HAVING
        if !self.having_conditions.is_empty() {
            query_parts.push(format!("HAVING {}", self.having_conditions.join(" AND ")));
        }

        // Add ORDER BY
        if !self.order_by.is_empty() {
            query_parts.push(format!("ORDER BY {}", self.order_by.join(", ")));
        }

        // Add LIMIT
        if let Some(limit) = self.limit_clause {
            query_parts.push(format!("LIMIT {}", limit));
        }

        // Add OFFSET
        if let Some(offset) = self.offset_clause {
            query_parts.push(format!("OFFSET {}", offset));
        }

        self.query.query = query_parts.join(" ");
        Ok(self.query)
    }
}

/// INSERT query builder
///
/// Provides a fluent interface for building INSERT queries with support for
/// multiple value sets, conflict resolution, and RETURNING clauses.
#[derive(Debug, Clone)]
pub struct InsertBuilder {
    /// The underlying query builder
    query: QueryBuilder,
    /// Target table name
    table: String,
    /// Column names for insertion
    columns: Vec<String>,
    /// Value placeholders for each row to insert
    values: Vec<Vec<String>>,
    /// ON CONFLICT clause for handling constraint violations
    on_conflict: Option<String>,
    /// RETURNING clause for getting inserted data back
    returning: Option<String>,
}

impl InsertBuilder {
    /// Add a single row of values
    pub fn values<T>(mut self, values: &[(&str, T)]) -> Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send + Clone,
    {
        if self.columns.is_empty() {
            self.columns = values.iter().map(|(col, _)| col.to_string()).collect();
        }

        let placeholders: Vec<String> = values
            .iter()
            .map(|(_, value)| {
                let placeholder = format!("${}", self.query.args.len() + 1);
                self.query.bind(value.clone());
                placeholder
            })
            .collect();

        self.values.push(placeholders);
        self
    }

    /// Add ON CONFLICT clause
    pub fn on_conflict(mut self, conflict: &str) -> Self {
        self.on_conflict = Some(conflict.to_string());
        self
    }

    /// Add RETURNING clause
    pub fn returning(mut self, columns: &str) -> Self {
        self.returning = Some(columns.to_string());
        self
    }

    /// Build the final query
    pub fn build(mut self) -> DatabaseResult<QueryBuilder> {
        if self.columns.is_empty() || self.values.is_empty() {
            return Err(DatabaseError::Query {
                query: "incomplete".to_string(),
                message: "INSERT query must have columns and values".to_string(),
            });
        }

        let mut query_parts = vec![
            format!("INSERT INTO {} ({})", self.table, self.columns.join(", ")),
            format!(
                "VALUES {}",
                self.values
                    .iter()
                    .map(|row| format!("({})", row.join(", ")))
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
        ];

        if let Some(conflict) = &self.on_conflict {
            query_parts.push(format!("ON CONFLICT {}", conflict));
        }

        if let Some(returning) = &self.returning {
            query_parts.push(format!("RETURNING {}", returning));
        }

        self.query.query = query_parts.join(" ");
        Ok(self.query)
    }
}

/// UPDATE query builder
///
/// Provides a fluent interface for building UPDATE queries with support for
///
/// SET clauses, WHERE conditions, and RETURNING clauses.
#[derive(Debug, Clone)]
pub struct UpdateBuilder {
    /// The underlying query builder
    query: QueryBuilder,
    /// Target table name
    table: String,
    /// SET clauses for column updates
    set_clauses: Vec<String>,
    /// WHERE conditions to filter rows for update
    where_conditions: Vec<String>,
    /// RETURNING clause for getting updated data back
    returning: Option<String>,
}

impl UpdateBuilder {
    /// Add SET clause
    pub fn set<T>(mut self, column: &str, value: T) -> Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        let placeholder = format!("${}", self.query.args.len() + 1);
        self.set_clauses
            .push(format!("{} = {}", column, placeholder));
        self.query.bind(value);
        self
    }

    /// Add WHERE condition
    pub fn where_eq<T>(mut self, column: &str, value: T) -> Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        let placeholder = format!("${}", self.query.args.len() + 1);
        self.where_conditions
            .push(format!("{} = {}", column, placeholder));
        self.query.bind(value);
        self
    }

    /// Add RETURNING clause
    pub fn returning(mut self, columns: &str) -> Self {
        self.returning = Some(columns.to_string());
        self
    }

    /// Build the final query
    pub fn build(mut self) -> DatabaseResult<QueryBuilder> {
        if self.set_clauses.is_empty() {
            return Err(DatabaseError::Query {
                query: "incomplete".to_string(),
                message: "UPDATE query must have SET clauses".to_string(),
            });
        }

        let mut query_parts = vec![
            format!("UPDATE {}", self.table),
            format!("SET {}", self.set_clauses.join(", ")),
        ];

        if !self.where_conditions.is_empty() {
            query_parts.push(format!("WHERE {}", self.where_conditions.join(" AND ")));
        }

        if let Some(returning) = &self.returning {
            query_parts.push(format!("RETURNING {}", returning));
        }

        self.query.query = query_parts.join(" ");
        Ok(self.query)
    }
}

/// DELETE query builder
///
/// Provides a fluent interface for building DELETE queries with support for
///
/// WHERE conditions and RETURNING clauses.
#[derive(Debug, Clone)]
pub struct DeleteBuilder {
    /// The underlying query builder
    query: QueryBuilder,
    /// Target table name
    table: String,
    /// WHERE conditions to filter rows for deletion
    where_conditions: Vec<String>,
    /// RETURNING clause for getting deleted data back
    returning: Option<String>,
}

impl DeleteBuilder {
    /// Add WHERE condition
    pub fn where_eq<T>(mut self, column: &str, value: T) -> Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        let placeholder = format!("${}", self.query.args.len() + 1);
        self.where_conditions
            .push(format!("{} = {}", column, placeholder));
        self.query.bind(value);
        self
    }

    /// Add custom WHERE condition
    pub fn where_raw(mut self, condition: &str) -> Self {
        self.where_conditions.push(condition.to_string());
        self
    }

    /// Add RETURNING clause
    pub fn returning(mut self, columns: &str) -> Self {
        self.returning = Some(columns.to_string());
        self
    }

    /// Build the final query
    pub fn build(mut self) -> DatabaseResult<QueryBuilder> {
        let mut query_parts = vec![format!("DELETE FROM {}", self.table)];

        if !self.where_conditions.is_empty() {
            query_parts.push(format!("WHERE {}", self.where_conditions.join(" AND ")));
        }

        if let Some(returning) = &self.returning {
            query_parts.push(format!("RETURNING {}", returning));
        }

        self.query.query = query_parts.join(" ");
        Ok(self.query)
    }
}

/// Order direction for ORDER BY clauses
///
/// Specifies whether to sort in ascending or descending order.
#[derive(Debug, Clone, Copy)]
pub enum OrderDirection {
    /// Ascending order (smallest to largest)
    Asc,
    /// Descending order (largest to smallest)
    Desc,
}

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

/// Helper for building dynamic WHERE conditions
///
/// Provides a convenient way to build complex WHERE clauses dynamically
/// with proper parameter binding and type safety.
#[derive(Debug, Clone)]
pub struct WhereBuilder {
    /// List of WHERE conditions that will be joined with AND
    conditions: Vec<String>,
    /// Bound arguments for the WHERE conditions
    args: PgArguments,
}

impl WhereBuilder {
    /// Create a new WHERE builder
    pub fn new() -> Self {
        Self {
            conditions: Vec::new(),
            args: PgArguments::default(),
        }
    }

    /// Add an equality condition
    pub fn eq<T>(&mut self, column: &str, value: T) -> &mut Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        let placeholder = format!("${}", self.args.len() + 1);
        self.conditions
            .push(format!("{} = {}", column, placeholder));
        let _ = self.args.add(value);
        self
    }

    /// Add an IN condition
    pub fn in_values<T>(&mut self, column: &str, values: Vec<T>) -> &mut Self
    where
        T: for<'q> sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres> + Send,
    {
        if values.is_empty() {
            return self;
        }

        let placeholders: Vec<String> = values
            .into_iter()
            .map(|value| {
                let placeholder = format!("${}", self.args.len() + 1);
                let _ = self.args.add(value);
                placeholder
            })
            .collect();

        self.conditions
            .push(format!("{} IN ({})", column, placeholders.join(", ")));
        self
    }

    /// Add a custom condition
    pub fn custom(&mut self, condition: &str) -> &mut Self {
        self.conditions.push(condition.to_string());
        self
    }

    /// Build the WHERE clause
    pub fn build(self) -> (String, PgArguments) {
        let clause = if self.conditions.is_empty() {
            String::new()
        } else {
            format!("WHERE {}", self.conditions.join(" AND "))
        };
        (clause, self.args)
    }
}

impl Default for WhereBuilder {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_select_builder() {
        let query = QueryBuilder::select(&["id", "name"])
            .from("users")
            .where_eq("active", true)
            .order_by("name", OrderDirection::Asc)
            .limit(10)
            .build()
            .unwrap();

        assert!(query.sql().contains("SELECT id, name"));
        assert!(query.sql().contains("FROM users"));
        assert!(query.sql().contains("WHERE active = $1"));
        assert!(query.sql().contains("ORDER BY name ASC"));
        assert!(query.sql().contains("LIMIT 10"));
    }

    #[test]
    fn test_insert_builder() {
        let query = QueryBuilder::insert("users")
            .values(&[("name", "John"), ("email", "john@example.com")])
            .returning("id")
            .build()
            .unwrap();

        assert!(query.sql().contains("INSERT INTO users"));
        assert!(query.sql().contains("(name, email)"));
        assert!(query.sql().contains("VALUES ($1, $2)"));
        assert!(query.sql().contains("RETURNING id"));
    }

    #[test]
    fn test_update_builder() {
        let query = QueryBuilder::update("users")
            .set("name", "Jane")
            .set("updated_at", "NOW()")
            .where_eq("id", 1)
            .build()
            .unwrap();

        assert!(query.sql().contains("UPDATE users"));
        assert!(query.sql().contains("SET name = $1"));
        assert!(query.sql().contains("WHERE id = $"));
    }

    #[test]
    fn test_delete_builder() {
        let query = QueryBuilder::delete("users")
            .where_eq("id", 1)
            .returning("name")
            .build()
            .unwrap();

        assert!(query.sql().contains("DELETE FROM users"));
        assert!(query.sql().contains("WHERE id = $1"));
        assert!(query.sql().contains("RETURNING name"));
    }

    #[test]
    fn test_where_builder() {
        let mut where_builder = WhereBuilder::new();
        where_builder.eq("active", true);
        where_builder.in_values("status", vec!["active", "pending"]);
        where_builder.custom("created_at > NOW() - INTERVAL '1 day'");

        let (clause, _) = where_builder.build();
        assert!(clause.contains("WHERE"));
        assert!(clause.contains("active = $1"));
        assert!(clause.contains("status IN"));
        assert!(clause.contains("created_at >"));
    }
}
