mod common;
use common::*;

#[tokio::test]
async fn set_texture_returns_ok() {
    let (_temp, state, token) = setup();
    let app = router(state);

    let body = serde_json::json!({
        "name": "observation",
        "description": "Something noticed or perceived",
        "prompt": "Describe what you observed."
    });

    let response = app
        .oneshot(put_json_auth("/textures", &body, &token))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::OK);

    let info: Texture = body_json(response).await;
    assert_eq!(info.name, TextureName::new("observation"));
    assert_eq!(info.description.as_str(), "Something noticed or perceived");
    assert_eq!(info.prompt.as_str(), "Describe what you observed.");
}

#[tokio::test]
async fn set_texture_is_idempotent() {
    let (_temp, state, token) = setup();

    let body = serde_json::json!({
        "name": "insight",
        "description": "Version 1",
        "prompt": "Prompt v1"
    });

    let app = router(state.clone());
    let response = app
        .oneshot(put_json_auth("/textures", &body, &token))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::OK);

    let body2 = serde_json::json!({
        "name": "insight",
        "description": "Version 2",
        "prompt": "Prompt v2"
    });
    let app = router(state.clone());
    let response = app
        .oneshot(put_json_auth("/textures", &body2, &token))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::OK);

    let app = router(state);
    let response = app
        .oneshot(get_auth("/textures/insight", &token))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::OK);

    let info: Texture = body_json(response).await;
    assert_eq!(info.description.as_str(), "Version 2");
    assert_eq!(info.prompt.as_str(), "Prompt v2");
}

#[tokio::test]
async fn list_textures_empty() {
    let (_temp, state, token) = setup();
    let app = router(state);

    let response = app.oneshot(get_auth("/textures", &token)).await.unwrap();
    assert_eq!(response.status(), StatusCode::OK);

    let list: Vec<Texture> = body_json(response).await;
    assert!(list.is_empty());
}

#[tokio::test]
async fn list_textures_after_set() {
    let (_temp, state, token) = setup();

    let app = router(state.clone());
    let body = serde_json::json!({ "name": "hope", "description": "", "prompt": "" });
    app.oneshot(put_json_auth("/textures", &body, &token))
        .await
        .unwrap();

    let app = router(state.clone());
    let body = serde_json::json!({ "name": "fear", "description": "", "prompt": "" });
    app.oneshot(put_json_auth("/textures", &body, &token))
        .await
        .unwrap();

    let app = router(state);
    let response = app.oneshot(get_auth("/textures", &token)).await.unwrap();
    assert_eq!(response.status(), StatusCode::OK);

    let list: Vec<Texture> = body_json(response).await;
    assert_eq!(list.len(), 2);
}

#[tokio::test]
async fn get_texture_not_found() {
    let (_temp, state, token) = setup();
    let app = router(state);

    let response = app
        .oneshot(get_auth("/textures/nonexistent", &token))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::NOT_FOUND);
}

#[tokio::test]
async fn remove_texture_then_gone() {
    let (_temp, state, token) = setup();

    let app = router(state.clone());
    let body = serde_json::json!({ "name": "ephemeral", "description": "", "prompt": "" });
    app.oneshot(put_json_auth("/textures", &body, &token))
        .await
        .unwrap();

    let app = router(state.clone());
    let response = app
        .oneshot(delete_auth("/textures/ephemeral", &token))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::OK);

    let app = router(state);
    let response = app
        .oneshot(get_auth("/textures/ephemeral", &token))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::NOT_FOUND);
}

#[tokio::test]
async fn texture_request_without_token_returns_unauthorized() {
    let (_temp, state, _token) = setup();
    let app = router(state);

    let request = Request::builder()
        .method(Method::GET)
        .uri("/textures")
        .body(Body::empty())
        .unwrap();
    let response = app.oneshot(request).await.unwrap();
    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[tokio::test]
async fn texture_request_with_invalid_token_returns_unauthorized() {
    let (_temp, state, _token) = setup();
    let app = router(state);

    let response = app
        .oneshot(get_auth("/textures", "bogus-token-value"))
        .await
        .unwrap();
    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
