use super::*;
use domain::models::UserRole;
use domain::value_objects::{Email, PasswordHash, Username};
use sqlx::SqlitePool;

async fn setup() -> (SqlitePool, SqliteUserRepository) {
    let pool = SqlitePool::connect(":memory:").await.unwrap();
    sqlx::query(
        "CREATE TABLE users (id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, created_at TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'standard', display_name TEXT, bio TEXT, avatar_path TEXT, banner_path TEXT, also_known_as TEXT)"
    )
    .execute(&pool)
    .await
    .unwrap();
    sqlx::query(
        "CREATE TABLE user_profile_fields (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL, position INTEGER NOT NULL DEFAULT 0)"
    )
    .execute(&pool)
    .await
    .unwrap();
    let repo = SqliteUserRepository::new(pool.clone());
    (pool, repo)
}

#[tokio::test]
async fn find_by_id_returns_none_when_not_found() {
    let (_, repo) = setup().await;
    let result = repo
        .find_by_id(&UserId::from_uuid(uuid::Uuid::new_v4()))
        .await
        .unwrap();
    assert!(result.is_none());
}

#[tokio::test]
async fn find_by_id_returns_user_when_found() {
    let (pool, repo) = setup().await;
    let id = uuid::Uuid::new_v4();
    sqlx::query(
        "INSERT INTO users (id, email, username, password_hash, created_at) VALUES (?, ?, ?, ?, ?)",
    )
    .bind(id.to_string())
    .bind("test@example.com")
    .bind("test")
    .bind("$argon2id$v=19$m=65536,t=2,p=1$fakesalt$fakehash")
    .bind("2026-01-01T00:00:00Z")
    .execute(&pool)
    .await
    .unwrap();

    let result = repo.find_by_id(&UserId::from_uuid(id)).await.unwrap();
    assert!(result.is_some());
    assert_eq!(result.unwrap().email().value(), "test@example.com");
}

#[tokio::test]
async fn update_profile_persists_bio_and_avatar() {
    let (_, repo) = setup().await;
    let user = domain::models::User::new(
        Email::new("test@example.com".to_string()).unwrap(),
        Username::new("testuser".to_string()).unwrap(),
        PasswordHash::new("hash".to_string()).unwrap(),
        UserRole::Standard,
    );
    repo.save(&user).await.unwrap();

    repo.update_profile(
        user.id(),
        &domain::models::UserProfile {
            bio: Some("My biography".to_string()),
            avatar_path: Some("avatars/user1".to_string()),
            ..Default::default()
        },
    )
    .await
    .unwrap();

    let found = repo.find_by_id(user.id()).await.unwrap().unwrap();
    assert_eq!(found.bio(), Some("My biography"));
    assert_eq!(found.avatar_path(), Some("avatars/user1"));
}

#[tokio::test]
async fn update_profile_clears_fields_with_none() {
    let (_, repo) = setup().await;
    let user = domain::models::User::new(
        Email::new("test2@example.com".to_string()).unwrap(),
        Username::new("testuser2".to_string()).unwrap(),
        PasswordHash::new("hash".to_string()).unwrap(),
        UserRole::Standard,
    );
    repo.save(&user).await.unwrap();
    repo.update_profile(
        user.id(),
        &domain::models::UserProfile {
            bio: Some("bio".to_string()),
            avatar_path: Some("path".to_string()),
            ..Default::default()
        },
    )
    .await
    .unwrap();
    repo.update_profile(user.id(), &domain::models::UserProfile::default())
        .await
        .unwrap();

    let found = repo.find_by_id(user.id()).await.unwrap().unwrap();
    assert_eq!(found.bio(), None);
    assert_eq!(found.avatar_path(), None);
}
