mod common;
use common::*;
use pgmold::dump::generate_dump;

#[tokio::test]
async fn dump_roundtrip() {
    let (_container, url) = setup_postgres().await;
    let connection = PgConnection::new(&url).await.unwrap();

    sqlx::query("CREATE TYPE status AS ENUM ('active', 'inactive')")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("CREATE TABLE users (id BIGINT PRIMARY KEY, email TEXT NOT NULL, status status DEFAULT 'active')")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("CREATE INDEX users_email_idx ON users (email)")
        .execute(connection.pool())
        .await
        .unwrap();

    let schema = introspect_schema(&connection, &["public".to_string()], false)
        .await
        .unwrap();

    let dump = generate_dump(&schema, None);

    assert!(dump.contains("CREATE TYPE"), "dump should contain enum");
    assert!(dump.contains("CREATE TABLE"), "dump should contain table");
    assert!(dump.contains("CREATE INDEX"), "dump should contain index");
    assert!(dump.contains("users"), "dump should reference users table");
    assert!(dump.contains("status"), "dump should reference status enum");
}

#[tokio::test]
async fn dump_multi_schema() {
    let (_container, url) = setup_postgres().await;
    let connection = PgConnection::new(&url).await.unwrap();

    sqlx::query("CREATE SCHEMA auth")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("CREATE TABLE auth.users (id BIGINT PRIMARY KEY, email TEXT NOT NULL)")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("CREATE TABLE public.posts (id BIGINT PRIMARY KEY, user_id BIGINT REFERENCES auth.users(id))")
        .execute(connection.pool())
        .await
        .unwrap();

    let schema = introspect_schema(
        &connection,
        &["public".to_string(), "auth".to_string()],
        false,
    )
    .await
    .unwrap();

    let dump = generate_dump(&schema, None);

    assert!(
        dump.contains(r#""auth"."users""#),
        "dump should contain auth.users"
    );
    assert!(
        dump.contains(r#""public"."posts""#),
        "dump should contain public.posts"
    );
    assert!(
        dump.contains("REFERENCES"),
        "dump should contain FK reference"
    );
}

#[tokio::test]
async fn dump_complex_schema() {
    let (_container, url) = setup_postgres().await;
    let connection = PgConnection::new(&url).await.unwrap();

    sqlx::query("CREATE TABLE users (id BIGINT PRIMARY KEY, email TEXT)")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("CREATE FUNCTION get_user_count() RETURNS INTEGER AS $$ SELECT COUNT(*)::INTEGER FROM users; $$ LANGUAGE SQL STABLE")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("CREATE VIEW active_users AS SELECT * FROM users WHERE id > 0")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("ALTER TABLE users ENABLE ROW LEVEL SECURITY")
        .execute(connection.pool())
        .await
        .unwrap();
    sqlx::query("CREATE POLICY users_select ON users FOR SELECT USING (true)")
        .execute(connection.pool())
        .await
        .unwrap();

    let schema = introspect_schema(&connection, &["public".to_string()], false)
        .await
        .unwrap();

    let dump = generate_dump(&schema, None);

    assert!(
        dump.contains("CREATE TABLE"),
        "dump should contain CREATE TABLE"
    );
    assert!(
        dump.contains("CREATE FUNCTION") || dump.contains("CREATE OR REPLACE FUNCTION"),
        "dump should contain function"
    );
    assert!(
        dump.contains("CREATE VIEW") || dump.contains("CREATE OR REPLACE VIEW"),
        "dump should contain view"
    );
    assert!(
        dump.contains("ENABLE ROW LEVEL SECURITY"),
        "dump should contain RLS"
    );
    assert!(dump.contains("CREATE POLICY"), "dump should contain policy");
}

#[test]
fn dump_includes_default_privileges() {
    use pgmold::model::{DefaultPrivilege, DefaultPrivilegeObjectType, Privilege, Schema};
    use std::collections::BTreeSet;

    let mut schema = Schema::new();
    let mut privs = BTreeSet::new();
    privs.insert(Privilege::Select);
    privs.insert(Privilege::Insert);
    schema.default_privileges.push(DefaultPrivilege {
        target_role: "admin".to_string(),
        schema: Some("public".to_string()),
        object_type: DefaultPrivilegeObjectType::Tables,
        grantee: "app_user".to_string(),
        privileges: privs,
        with_grant_option: false,
    });

    let sql = generate_dump(&schema, None);

    assert!(
        sql.contains("ALTER DEFAULT PRIVILEGES"),
        "Dump should include ALTER DEFAULT PRIVILEGES. SQL:\n{sql}"
    );
    assert!(
        sql.contains("FOR ROLE"),
        "Dump should include FOR ROLE. SQL:\n{sql}"
    );
}
