use drizzle_core::Token;
// Re-export common enums and traits from core
pub use drizzle_core::builder::{BuilderInit, ExecutableState};
pub use drizzle_core::{
    OrderBy, SQL, ToSQL,
    traits::{SQLSchema, SQLTable},
};

// Local imports
use crate::{common::PostgresSchemaType, traits::PostgresTable, values::PostgresValue};
use core::{fmt::Debug, marker::PhantomData};

// Import modules - these provide specific builder types
pub mod cte;
pub mod delete;
pub mod insert;
pub mod prepared;
pub mod refresh;
pub mod select;
pub mod update;

// Re-export CTE types
pub use cte::{CTEDefinition, CTEView};

// Export state markers for easier use
pub use delete::{DeleteInitial, DeleteReturningSet, DeleteWhereSet};
pub use insert::{
    InsertDoUpdateSet, InsertInitial, InsertOnConflictSet, InsertReturningSet, InsertValuesSet,
    OnConflictBuilder,
};
pub use refresh::{
    RefreshConcurrently, RefreshInitial, RefreshMaterializedView, RefreshWithNoData,
    refresh_materialized_view,
};
pub use select::{
    SelectForSet, SelectFromSet, SelectGroupSet, SelectInitial, SelectJoinSet, SelectLimitSet,
    SelectOffsetSet, SelectOrderSet, SelectSetOpSet, SelectWhereSet,
};
pub use update::{
    UpdateFromSet, UpdateInitial, UpdateReturningSet, UpdateSetClauseSet, UpdateWhereSet,
};

// Re-export SQLViewInfo for convenience when using refresh_materialized_view
pub use drizzle_core::traits::SQLViewInfo;

#[derive(Debug, Clone)]
pub struct CTEInit;

impl ExecutableState for CTEInit {}

/// Main query builder for `PostgreSQL`
///
/// The `S` type parameter represents the schema type, which is used
/// to ensure type safety when building queries.
#[derive(Debug, Clone, Default)]
pub struct QueryBuilder<
    'a,
    Schema = (),
    State = (),
    Table = (),
    Marker = (),
    Row = (),
    Grouped = (),
> {
    pub sql: SQL<'a, PostgresValue<'a>>,
    schema: PhantomData<Schema>,
    state: PhantomData<State>,
    table: PhantomData<Table>,
    marker: PhantomData<Marker>,
    row: PhantomData<Row>,
    grouped: PhantomData<Grouped>,
}

//------------------------------------------------------------------------------
// QueryBuilder Implementation
//------------------------------------------------------------------------------

impl<'a, Schema, State, Table, Marker, Row, Grouped> ToSQL<'a, PostgresValue<'a>>
    for QueryBuilder<'a, Schema, State, Table, Marker, Row, Grouped>
{
    fn to_sql(&self) -> SQL<'a, PostgresValue<'a>> {
        self.sql.clone()
    }
}

impl<'a, Schema, State, Table, Marker, Row, Grouped>
    QueryBuilder<'a, Schema, State, Table, Marker, Row, Grouped>
where
    State: ExecutableState,
{
    /// Attaches a [sqlcommenter](https://google.github.io/sqlcommenter/) comment
    /// to the query.
    ///
    /// The comment is prepended to the generated SQL and wrapped in `/* ... */`.
    /// Any `/*` or `*/` sequences in the input are sanitised so they can't
    /// terminate the surrounding comment.
    #[must_use]
    pub fn comment(mut self, text: impl AsRef<str>) -> Self {
        let fragment = drizzle_core::sql::comment::<PostgresValue<'a>>(text);
        if fragment.chunks.is_empty() {
            return self;
        }
        let existing = core::mem::replace(&mut self.sql, fragment);
        self.sql.append_mut(existing);
        self
    }

    /// Attaches a tag-style [sqlcommenter](https://google.github.io/sqlcommenter/)
    /// comment to the query.
    ///
    /// Each `(key, value)` pair is URL-encoded, sorted alphabetically, joined
    /// with `,`, and wrapped in `/* ... */`. Pairs with empty values are
    /// skipped; an all-empty input is a no-op.
    #[must_use]
    pub fn comment_tags<I, K, V>(mut self, pairs: I) -> Self
    where
        I: IntoIterator<Item = (K, V)>,
        K: AsRef<str>,
        V: AsRef<str>,
    {
        let fragment = drizzle_core::sql::comment_tags::<PostgresValue<'a>, _, _, _>(pairs);
        if fragment.chunks.is_empty() {
            return self;
        }
        let existing = core::mem::replace(&mut self.sql, fragment);
        self.sql.append_mut(existing);
        self
    }
}

impl<'a> QueryBuilder<'a> {
    /// Creates a new query builder for the given schema
    #[must_use]
    pub const fn new<S>() -> QueryBuilder<'a, S, BuilderInit> {
        QueryBuilder {
            sql: SQL::empty(),
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }
}

impl<'a, Schema> QueryBuilder<'a, Schema, BuilderInit> {
    /// Begins a SELECT query with the specified columns.
    ///
    /// Pass individual columns, tuples of columns, or `()` to select all columns.
    pub fn select<T>(
        &self,
        columns: T,
    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial, (), T::Marker>
    where
        T: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
    {
        let sql = crate::helpers::select(columns);
        select::SelectBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins a SELECT DISTINCT query with the specified columns.
    ///
    /// SELECT DISTINCT removes duplicate rows from the result set.
    pub fn select_distinct<T>(
        &self,
        columns: T,
    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial, (), T::Marker>
    where
        T: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
    {
        let sql = crate::helpers::select_distinct(columns);
        select::SelectBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins a SELECT DISTINCT ON query (PostgreSQL-specific).
    ///
    /// Returns one row per distinct combination of the `on` columns.
    /// Use with `order_by` to control which row is kept for each group.
    pub fn select_distinct_on<On, Columns>(
        &self,
        on: On,
        columns: Columns,
    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial, (), Columns::Marker>
    where
        On: ToSQL<'a, PostgresValue<'a>>,
        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
    {
        let sql = crate::helpers::select_distinct_on(on, columns);
        select::SelectBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }
}

impl<'a, Schema> QueryBuilder<'a, Schema, CTEInit> {
    /// Begins a SELECT query with the specified columns after a CTE.
    pub fn select<T>(
        &self,
        columns: T,
    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial, (), T::Marker>
    where
        T: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
    {
        let sql = self.sql.clone().append(crate::helpers::select(columns));
        select::SelectBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins a SELECT DISTINCT query with the specified columns after a CTE.
    pub fn select_distinct<T>(
        &self,
        columns: T,
    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial, (), T::Marker>
    where
        T: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
    {
        let sql = self
            .sql
            .clone()
            .append(crate::helpers::select_distinct(columns));
        select::SelectBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins a SELECT DISTINCT ON query with the specified columns after a CTE.
    pub fn select_distinct_on<On, Columns>(
        &self,
        on: On,
        columns: Columns,
    ) -> select::SelectBuilder<'a, Schema, select::SelectInitial, (), Columns::Marker>
    where
        On: ToSQL<'a, PostgresValue<'a>>,
        Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
    {
        let sql = self
            .sql
            .clone()
            .append(crate::helpers::select_distinct_on(on, columns));
        select::SelectBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins an INSERT query after a CTE.
    pub fn insert<Table>(
        &self,
        table: Table,
    ) -> insert::InsertBuilder<'a, Schema, insert::InsertInitial, Table>
    where
        Table: PostgresTable<'a>,
    {
        let sql = self
            .sql
            .clone()
            .append(crate::helpers::insert::<Table>(&table));

        insert::InsertBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins an UPDATE query after a CTE.
    pub fn update<Table>(
        &self,
        table: Table,
    ) -> update::UpdateBuilder<'a, Schema, update::UpdateInitial, Table>
    where
        Table: PostgresTable<'a>,
    {
        let sql = self.sql.clone().append(crate::helpers::update::<
            Table,
            PostgresSchemaType,
            PostgresValue<'a>,
        >(&table));

        update::UpdateBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins a DELETE query after a CTE.
    pub fn delete<Table>(
        &self,
        table: Table,
    ) -> delete::DeleteBuilder<'a, Schema, delete::DeleteInitial, Table>
    where
        Table: PostgresTable<'a>,
    {
        let sql = self.sql.clone().append(crate::helpers::delete::<
            Table,
            PostgresSchemaType,
            PostgresValue<'a>,
        >(&table));

        delete::DeleteBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    #[must_use]
    pub fn with<C>(&self, cte: &C) -> Self
    where
        C: CTEDefinition<'a>,
    {
        let sql = self
            .sql
            .clone()
            .push(Token::COMMA)
            .append(cte.cte_definition());
        QueryBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }
}

impl<'a, Schema> QueryBuilder<'a, Schema, BuilderInit> {
    /// Begins an INSERT query for the specified table.
    pub fn insert<Table>(
        &self,
        table: Table,
    ) -> insert::InsertBuilder<'a, Schema, insert::InsertInitial, Table>
    where
        Table: PostgresTable<'a>,
    {
        let sql = crate::helpers::insert::<Table>(&table);

        insert::InsertBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins an UPDATE query for the specified table.
    pub fn update<Table>(
        &self,
        table: Table,
    ) -> update::UpdateBuilder<'a, Schema, update::UpdateInitial, Table>
    where
        Table: PostgresTable<'a>,
    {
        let sql = crate::helpers::update::<Table, PostgresSchemaType, PostgresValue<'a>>(&table);

        update::UpdateBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Begins a DELETE query for the specified table.
    pub fn delete<Table>(
        &self,
        table: Table,
    ) -> delete::DeleteBuilder<'a, Schema, delete::DeleteInitial, Table>
    where
        Table: PostgresTable<'a>,
    {
        let sql = crate::helpers::delete::<Table, PostgresSchemaType, PostgresValue<'a>>(&table);

        delete::DeleteBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }

    /// Starts a WITH (CTE) clause. Chain additional `.with()` calls to add more CTEs.
    pub fn with<C>(&self, cte: &C) -> QueryBuilder<'a, Schema, CTEInit>
    where
        C: CTEDefinition<'a>,
    {
        let sql = SQL::from(Token::WITH).append(cte.cte_definition());
        QueryBuilder {
            sql,
            schema: PhantomData,
            state: PhantomData,
            table: PhantomData,
            marker: PhantomData,
            row: PhantomData,
            grouped: PhantomData,
        }
    }
}

// Marker trait to indicate a query builder state is executable
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_query_builder_new() {
        let qb = QueryBuilder::new::<()>();
        let sql = qb.to_sql();
        assert_eq!(sql.sql(), "");
        assert_eq!(sql.params().count(), 0);
    }

    #[test]
    fn test_builder_init_type() {
        let _state = BuilderInit;
    }
}
