//! Tests for FormatStruct sqlx IO integration.

use mad_hatter::FormatStruct;
use sqlx::SqlitePool;

#[derive(Debug, Clone, PartialEq, Eq, FormatStruct)]
#[format("{prefix}:{value}")]
struct TestTag {
    prefix: String,
    value: String,
}

#[derive(Debug, Clone, PartialEq, Eq, FormatStruct)]
#[format("{name}@{version}")]
struct PkgRef {
    name: String,
    version: Option<String>,
}

#[derive(Debug, sqlx::FromRow)]
struct TagRow {
    id: i32,
    tag: TestTag,
}

async fn setup_pool() -> SqlitePool {
    let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
    sqlx::query("CREATE TABLE test_tags (id INTEGER PRIMARY KEY AUTOINCREMENT, tag TEXT NOT NULL)")
        .execute(&pool)
        .await
        .unwrap();
    pool
}

#[tokio::test]
async fn test_insert_and_select_format_struct() {
    let pool = setup_pool().await;
    let tag = TestTag { prefix: "env".to_owned(), value: "prod".to_owned() };

    sqlx::query("INSERT INTO test_tags (tag) VALUES (?)")
        .bind(&tag)
        .execute(&pool)
        .await
        .unwrap();

    let rows: Vec<TagRow> = sqlx::query_as("SELECT id, tag FROM test_tags")
        .fetch_all(&pool)
        .await
        .unwrap();

    assert_eq!(rows.len(), 1);
    assert_eq!(rows[0].tag.prefix, "env");
    assert_eq!(rows[0].tag.value, "prod");
    assert_eq!(rows[0].tag.to_string(), "env:prod");
}

#[tokio::test]
async fn test_multiple_insert_and_select() {
    let pool = setup_pool().await;
    let tag1 = TestTag { prefix: "env".to_owned(), value: "prod".to_owned() };
    let tag2 = TestTag { prefix: "env".to_owned(), value: "dev".to_owned() };

    sqlx::query("INSERT INTO test_tags (tag) VALUES (?)")
        .bind(&tag1)
        .execute(&pool)
        .await
        .unwrap();
    sqlx::query("INSERT INTO test_tags (tag) VALUES (?)")
        .bind(&tag2)
        .execute(&pool)
        .await
        .unwrap();

    let rows: Vec<TagRow> = sqlx::query_as("SELECT id, tag FROM test_tags ORDER BY id")
        .fetch_all(&pool)
        .await
        .unwrap();

    assert_eq!(rows.len(), 2);
    assert_eq!(rows[0].tag.to_string(), "env:prod");
    assert_eq!(rows[1].tag.to_string(), "env:dev");
}

#[tokio::test]
async fn test_filter_with_format_struct() {
    let pool = setup_pool().await;
    let tag1 = TestTag { prefix: "env".to_owned(), value: "prod".to_owned() };
    let tag2 = TestTag { prefix: "env".to_owned(), value: "dev".to_owned() };

    sqlx::query("INSERT INTO test_tags (tag) VALUES (?)")
        .bind(&tag1)
        .execute(&pool)
        .await
        .unwrap();
    sqlx::query("INSERT INTO test_tags (tag) VALUES (?)")
        .bind(&tag2)
        .execute(&pool)
        .await
        .unwrap();

    let rows: Vec<TagRow> = sqlx::query_as("SELECT id, tag FROM test_tags WHERE tag = ?")
        .bind(&tag1)
        .fetch_all(&pool)
        .await
        .unwrap();

    assert_eq!(rows.len(), 1);
    assert_eq!(rows[0].tag.prefix, "env");
    assert_eq!(rows[0].tag.value, "prod");
}

#[tokio::test]
async fn test_roundtrip_single_column() {
    let pool = setup_pool().await;
    let tag = TestTag { prefix: "db".to_owned(), value: "sqlite".to_owned() };

    sqlx::query("INSERT INTO test_tags (tag) VALUES (?)")
        .bind(&tag)
        .execute(&pool)
        .await
        .unwrap();

    let result: (TestTag,) = sqlx::query_as("SELECT tag FROM test_tags")
        .fetch_one(&pool)
        .await
        .unwrap();

    assert_eq!(result.0.to_string(), "db:sqlite");
    assert_eq!(result.0.prefix, "db");
    assert_eq!(result.0.value, "sqlite");
}

#[test]
fn test_option_field_roundtrip() {
    let pkg = PkgRef { name: "tokio".to_owned(), version: Some("1.0".to_owned()) };
    assert_eq!(pkg.to_string(), "tokio@1.0");

    let p1 = PkgRef::from("tokio@1.0".to_owned());
    assert_eq!(p1.name, "tokio");
    assert_eq!(p1.version, Some("1.0".to_owned()));

    let p2 = PkgRef::from("serde@".to_owned());
    assert_eq!(p2.name, "serde");
    assert_eq!(p2.version, None);
}