use crate::api::main::{create_user_with_password, setup};
use axum::http::{header, StatusCode};
use http_body_util::BodyExt;
use serde_json::{json, Value};
use utils::testing::{
    make_get_request, make_jwt_request, make_post_request, make_request_with_headers,
};

#[tokio::test]
async fn test_webfinger_discovery() {
    let app = setup().await;
    create_user_with_password(&app.db, "testuser", "password123", "testuser@example.com").await;

    // 1. Valid WebFinger lookup for existing user
    let url = "/.well-known/webfinger?resource=acct:testuser@localhost:3000";
    let response = make_get_request(app.router.clone(), url, None).await;
    assert_eq!(response.status(), StatusCode::OK);
    let body = response.into_body().collect().await.unwrap().to_bytes();
    let v: Value = serde_json::from_slice(&body).unwrap();
    assert_eq!(v["subject"], "acct:testuser@localhost:3000");
    assert_eq!(
        v["links"][0]["href"],
        "http://localhost:3000/users/testuser"
    );

    // 2. WebFinger lookup for a non-existent user
    let response = make_get_request(
        app.router.clone(),
        "/.well-known/webfinger?resource=acct:nobody@localhost:3000",
        None,
    )
    .await;
    assert_eq!(response.status(), StatusCode::NOT_FOUND);
}

#[tokio::test]
async fn test_user_actor_endpoint() {
    let app = setup().await;
    create_user_with_password(&app.db, "testuser", "password123", "testuser@example.com").await;

    let response = make_request_with_headers(
        app.router.clone(),
        "/users/testuser",
        "GET",
        None,
        vec![(
            header::ACCEPT,
            "application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
        )],
    ).await;

    assert_eq!(response.status(), StatusCode::OK);
    let content_type = response.headers().get(header::CONTENT_TYPE).unwrap();
    assert_eq!(content_type, "application/activity+json");

    let body = response.into_body().collect().await.unwrap().to_bytes();
    let v: Value = serde_json::from_slice(&body).unwrap();
    assert_eq!(v["type"], "Person");
    assert_eq!(v["preferredUsername"], "testuser");
    assert_eq!(v["id"], "http://localhost:3000/users/testuser");
}

#[tokio::test]
async fn test_user_inbox_follow() {
    let app = setup().await;
    // user1 will be followed
    let user1 =
        create_user_with_password(&app.db, "user1", "password123", "user1@example.com").await;
    // user2 will be the follower
    let user2 =
        create_user_with_password(&app.db, "user2", "password123", "user2@example.com").await;

    // Construct a follow activity from user2, targeting user1
    let follow_activity = json!({
      "@context": "https://www.w3.org/ns/activitystreams",
      "id": "http://localhost:3000/some-unique-id",
      "type": "Follow",
      "actor": "http://localhost:3000/users/user2", // The actor is user2
      "object": "http://localhost:3000/users/user1"
    })
    .to_string();

    // POST the activity to user1's inbox
    let response = make_post_request(
        app.router.clone(),
        "/users/user1/inbox",
        follow_activity,
        None,
    )
    .await;

    assert_eq!(response.status(), StatusCode::ACCEPTED);

    // Verify that user2 is now following user1 in the database
    let followers = app::persistence::follow::get_following_ids(&app.db, user2.id)
        .await
        .unwrap();
    assert!(
        followers.contains(&user1.id),
        "User2 should be following user1"
    );

    let following = app::persistence::follow::get_following_ids(&app.db, user1.id)
        .await
        .unwrap();
    assert!(
        !following.contains(&user2.id),
        "User1 should now be followed by user2"
    );
    assert!(following.is_empty(), "User1 should not be following anyone");
}

#[tokio::test]
async fn test_user_outbox_get() {
    let app = setup().await;
    create_user_with_password(&app.db, "testuser", "password123", "testuser@example.com").await;
    let token = super::main::login_user(app.router.clone(), "testuser", "password123").await;

    // Create a thought first
    let thought_body = json!({ "content": "This is a federated thought!" }).to_string();
    make_jwt_request(
        app.router.clone(),
        "/thoughts",
        "POST",
        Some(thought_body),
        &token,
    )
    .await;

    // Now, fetch the outbox
    let response = make_request_with_headers(
        app.router.clone(),
        "/users/testuser/outbox",
        "GET",
        None,
        vec![(header::ACCEPT, "application/activity+json")],
    )
    .await;

    assert_eq!(response.status(), StatusCode::OK);
    let body = response.into_body().collect().await.unwrap().to_bytes();
    let v: Value = serde_json::from_slice(&body).unwrap();

    assert_eq!(v["type"], "OrderedCollection");
    assert_eq!(v["totalItems"], 1);
    assert_eq!(v["orderedItems"][0]["type"], "Create");
    assert_eq!(
        v["orderedItems"][0]["object"]["content"],
        "This is a federated thought!"
    );
}
