//! Test utilities for creating test databases and seeding test data.

use crate::App;
use axum::extract::{DefaultBodyLimit, Multipart, Path, State};
use axum::routing::{get, post};
use axum::Router;
use sqlx::sqlite::SqlitePoolOptions;
use std::sync::Arc;

/// Create an App instance with an in-memory SQLite database for testing.
/// Migrations are applied automatically.
pub async fn create_test_app() -> Arc<App> {
    let pool = SqlitePoolOptions::new()
        .max_connections(1)
        .connect("sqlite::memory:")
        .await
        .expect("Failed to create in-memory database");

    // Run migrations
    sqlx::migrate!("./migrations")
        .run(&pool)
        .await
        .expect("Failed to run migrations");

    Arc::new(App::from_pool(pool))
}

/// Insert a test photo into the database.
/// Returns the photo ID.
pub async fn seed_photo(app: &App, hash: &str, title: &str) -> i32 {
    let result = sqlx::query_scalar::<_, i32>(
        r#"
        INSERT INTO photo (hash, title, fullsize_path, websize_path, thumbnail_path)
        VALUES (?, ?, '/test/full.jpg', '/test/web.jpg', '/test/thumb.jpg')
        RETURNING id
        "#,
    )
    .bind(hash)
    .bind(title)
    .fetch_one(app.pool())
    .await
    .expect("Failed to insert test photo");

    result
}

/// Insert a test album into the database.
/// Returns the album ID.
pub async fn seed_album(app: &App, name: &str) -> i32 {
    let result = sqlx::query_scalar::<_, i32>(
        r#"
        INSERT INTO album (name)
        VALUES (?)
        RETURNING id
        "#,
    )
    .bind(name)
    .fetch_one(app.pool())
    .await
    .expect("Failed to insert test album");

    result
}

/// Link a photo to an album with a given position.
pub async fn seed_album_photo(app: &App, album_id: i32, photo_id: i32, position: i32) {
    sqlx::query(
        r#"
        INSERT INTO album_photo (album_id, photo_id, position)
        VALUES (?, ?, ?)
        "#,
    )
    .bind(album_id)
    .bind(photo_id)
    .bind(position)
    .execute(app.pool())
    .await
    .expect("Failed to link photo to album");
}

/// Set the current album in settings.
pub async fn set_current_album(app: &App, album_id: i32) {
    sqlx::query(
        r#"
        UPDATE settings SET current_album_id = ?, current_photo_index = 0 WHERE id = 1
        "#,
    )
    .bind(album_id)
    .execute(app.pool())
    .await
    .expect("Failed to set current album");
}

/// Create a test router that includes extra routes (upload, image serving).
/// This is useful for testing endpoints that aren't generated by the macro.
/// Includes a 50MB body limit to allow large photo uploads in tests.
pub fn create_test_router_with_extras(app: Arc<App>) -> Router {
    async fn serve_image(
        State(state): State<Arc<App>>,
        Path(id): Path<i32>,
    ) -> axum::response::Response {
        state.serve_image(id).await
    }

    async fn upload_photo(
        State(state): State<Arc<App>>,
        multipart: Multipart,
    ) -> axum::response::Response {
        state.upload_photo(multipart).await
    }

    let api_router = app.clone().router();
    let extra_routes = Router::new()
        .route("/api/images/{id}", get(serve_image))
        .route("/api/photos", post(upload_photo))
        .with_state(app);

    api_router
        .merge(extra_routes)
        .layer(DefaultBodyLimit::max(50 * 1024 * 1024)) // 50MB limit for uploads
}
