//! Medication-endpoint integration tests.
//!
//! In-process against an isolated MongoDB test DB (see `common`). When Mongo is
//! unreachable these skip gracefully. Verifies auth enforcement and an
//! authenticated create -> list flow with the actual API contract.

mod common;

use serde_json::json;

/// Skip when Mongo is unavailable (counts as a pass); CI provides Mongo.
macro_rules! require_app {
    ($app:expr) => {
        match $app {
            Some(x) => x,
            None => {
                eprintln!("[integration] skipped (MongoDB unavailable)");
                return;
            }
        }
    };
}

#[tokio::test]
async fn create_medication_requires_auth() {
    let (app, db_name) = require_app!(common::app_for_test().await);

    let (status, _) = common::send_json(
        &app,
        "POST",
        "/api/medications",
        Some(
            json!({ "name": "Test Med", "dosage": "10mg", "frequency": "daily", "route": "oral" }),
        ),
        None,
    )
    .await;
    assert_eq!(status, 401);

    common::drop_test_db(&db_name).await;
}

#[tokio::test]
async fn list_medications_requires_auth() {
    let (app, db_name) = require_app!(common::app_for_test().await);

    let (status, _) = common::send_json(&app, "GET", "/api/medications", None, None).await;
    assert_eq!(status, 401);

    common::drop_test_db(&db_name).await;
}

#[tokio::test]
async fn get_medication_requires_auth() {
    let (app, db_name) = require_app!(common::app_for_test().await);

    let (status, _) = common::send_json(
        &app,
        "GET",
        "/api/medications/507f1f77bcf86cd799439011",
        None,
        None,
    )
    .await;
    assert_eq!(status, 401);

    common::drop_test_db(&db_name).await;
}

#[tokio::test]
async fn authenticated_user_can_create_and_list_medication() {
    let (app, db_name) = require_app!(common::app_for_test().await);
    let email = unique_email();

    // Register and grab the access token.
    let (status, body) = common::send_json(
        &app,
        "POST",
        "/api/auth/register",
        Some(json!({ "email": email, "username": "tester", "password": "supersecret" })),
        None,
    )
    .await;
    assert_eq!(status, 201);
    let token = body["token"].as_str().unwrap().to_string();

    // Create a medication (name/dosage/frequency/route are all required).
    let (status, body) = common::send_json(
        &app,
        "POST",
        "/api/medications",
        Some(json!({
            "name": "Ibuprofen",
            "dosage": "200mg",
            "frequency": "as needed",
            "route": "oral"
        })),
        Some(&token),
    )
    .await;
    assert!(
        status == 200 || status == 201,
        "create should succeed, body: {body}"
    );

    // List medications for the user — should include the one we just created.
    let (status, body) =
        common::send_json(&app, "GET", "/api/medications", None, Some(&token)).await;
    assert_eq!(status, 200, "list should succeed, body: {body}");
    let empty = Vec::new();
    let arr = body.as_array().unwrap_or(&empty);
    assert!(
        !arr.is_empty(),
        "list should contain the created medication"
    );

    common::drop_test_db(&db_name).await;
}

fn unique_email() -> String {
    format!("test_{}@example.com", uuid::Uuid::new_v4())
}
