mod common;
use common::*;
use reqwest::StatusCode;
use serde_json::{Value, json};
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};

#[tokio::test]
async fn test_resolve_handle_success() {
    let client = client();
    let short_handle = format!("rt{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
    let payload = json!({
        "handle": short_handle,
        "email": format!("{}@example.com", short_handle),
        "password": "Testpass123!"
    });
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.server.createAccount",
            base_url().await
        ))
        .json(&payload)
        .send()
        .await
        .expect("Failed to create account");
    assert_eq!(res.status(), StatusCode::OK);
    let body: Value = res.json().await.expect("Invalid JSON");
    let did = body["did"].as_str().expect("No DID").to_string();
    let full_handle = body["handle"]
        .as_str()
        .expect("No handle in response")
        .to_string();
    let params = [("handle", full_handle.as_str())];
    let res = client
        .get(format!(
            "{}/xrpc/com.atproto.identity.resolveHandle",
            base_url().await
        ))
        .query(&params)
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::OK);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["did"], did);
}

#[tokio::test]
async fn test_resolve_handle_not_found() {
    let client = client();
    let _base = base_url().await;
    let params = [("handle", "nonexistent.handle.test")];
    let res = client
        .get(format!("{}/xrpc/com.atproto.identity.resolveHandle", _base))
        .query(&params)
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::NOT_FOUND);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "HandleNotFound");
}

#[tokio::test]
async fn test_resolve_handle_missing_param() {
    let client = client();
    let res = client
        .get(format!(
            "{}/xrpc/com.atproto.identity.resolveHandle",
            base_url().await
        ))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}

#[tokio::test]
async fn test_well_known_did() {
    let client = client();
    let res = client
        .get(format!("{}/.well-known/did.json", base_url().await))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::OK);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert!(body["id"].as_str().unwrap().starts_with("did:web:"));
    assert_eq!(body["service"][0]["type"], "AtprotoPersonalDataServer");
}

#[tokio::test]
async fn test_create_did_web_account_and_resolve() {
    let client = client();
    let mock_server = MockServer::start().await;
    let mock_uri = mock_server.uri();
    let mock_addr = mock_uri.trim_start_matches("http://");
    let did = format!("did:web:{}", mock_addr.replace(":", "%3A"));
    let handle = format!("wu{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
    let base = base_url().await;
    let pds_endpoint = common::pds_endpoint();

    let reserve_res = client
        .post(format!(
            "{}/xrpc/com.atproto.server.reserveSigningKey",
            base
        ))
        .json(&json!({ "did": did }))
        .send()
        .await
        .expect("Failed to reserve signing key");
    assert_eq!(reserve_res.status(), StatusCode::OK);
    let reserve_body: Value = reserve_res.json().await.expect("Response was not JSON");
    let signing_key = reserve_body["signingKey"]
        .as_str()
        .expect("No signingKey returned");
    let public_key_multibase = signing_key
        .strip_prefix("did:key:")
        .expect("signingKey should start with did:key:");

    let did_doc = json!({
        "@context": ["https://www.w3.org/ns/did/v1"],
        "id": did,
        "verificationMethod": [{
            "id": format!("{}#atproto", did),
            "type": "Multikey",
            "controller": did,
            "publicKeyMultibase": public_key_multibase
        }],
        "service": [{
            "id": "#atproto_pds",
            "type": "AtprotoPersonalDataServer",
            "serviceEndpoint": pds_endpoint
        }]
    });
    Mock::given(method("GET"))
        .and(path("/.well-known/did.json"))
        .respond_with(ResponseTemplate::new(200).set_body_json(did_doc))
        .mount(&mock_server)
        .await;
    let payload = json!({
        "handle": handle,
        "email": format!("{}@example.com", handle),
        "password": "Testpass123!",
        "did": did,
        "signingKey": signing_key
    });
    let res = client
        .post(format!("{}/xrpc/com.atproto.server.createAccount", base))
        .json(&payload)
        .send()
        .await
        .expect("Failed to send request");
    if res.status() != StatusCode::OK {
        let status = res.status();
        let body: Value = res
            .json()
            .await
            .unwrap_or(json!({"error": "could not parse body"}));
        panic!("createAccount failed with status {}: {:?}", status, body);
    }
    let body: Value = res
        .json()
        .await
        .expect("createAccount response was not JSON");
    assert_eq!(body["did"], did);
    let res = client
        .get(format!("{}/u/{}/did.json", base, handle))
        .send()
        .await
        .expect("Failed to fetch DID doc");
    assert_eq!(
        res.status(),
        StatusCode::NOT_FOUND,
        "External did:web should not have DID doc served by PDS (user hosts their own)"
    );
}

#[tokio::test]
async fn test_create_account_duplicate_handle() {
    let client = client();
    let handle = format!("dp{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
    let email = format!("{}@example.com", handle);
    let payload = json!({
        "handle": handle,
        "email": email,
        "password": "Testpass123!"
    });
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.server.createAccount",
            base_url().await
        ))
        .json(&payload)
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::OK);
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.server.createAccount",
            base_url().await
        ))
        .json(&payload)
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    let body: Value = res.json().await.expect("Response was not JSON");
    assert_eq!(body["error"], "HandleTaken");
}

#[tokio::test]
async fn test_did_web_lifecycle() {
    let client = client();
    let base = base_url().await;
    let mock_server = MockServer::start().await;
    let mock_uri = mock_server.uri();
    let mock_addr = mock_uri.trim_start_matches("http://");
    let handle = format!("lc{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
    let did = format!("did:web:{}:u:{}", mock_addr.replace(":", "%3A"), handle);
    let email = format!("{}@test.com", handle);
    let pds_endpoint = common::pds_endpoint();

    let reserve_res = client
        .post(format!(
            "{}/xrpc/com.atproto.server.reserveSigningKey",
            base
        ))
        .json(&json!({ "did": did }))
        .send()
        .await
        .expect("Failed to reserve signing key");
    assert_eq!(reserve_res.status(), StatusCode::OK);
    let reserve_body: Value = reserve_res.json().await.expect("Response was not JSON");
    let signing_key = reserve_body["signingKey"]
        .as_str()
        .expect("No signingKey returned");
    let public_key_multibase = signing_key
        .strip_prefix("did:key:")
        .expect("signingKey should start with did:key:");

    let did_doc = json!({
        "@context": ["https://www.w3.org/ns/did/v1"],
        "id": did,
        "verificationMethod": [{
            "id": format!("{}#atproto", did),
            "type": "Multikey",
            "controller": did,
            "publicKeyMultibase": public_key_multibase
        }],
        "service": [{
            "id": "#atproto_pds",
            "type": "AtprotoPersonalDataServer",
            "serviceEndpoint": pds_endpoint
        }]
    });
    Mock::given(method("GET"))
        .and(path(format!("/u/{}/did.json", handle)))
        .respond_with(ResponseTemplate::new(200).set_body_json(did_doc))
        .mount(&mock_server)
        .await;
    let create_payload = json!({
        "handle": handle,
        "email": email,
        "password": "Testpass123!",
        "did": did,
        "signingKey": signing_key
    });
    let res = client
        .post(format!("{}/xrpc/com.atproto.server.createAccount", base))
        .json(&create_payload)
        .send()
        .await
        .expect("Failed createAccount");
    if res.status() != StatusCode::OK {
        let body: Value = res.json().await.unwrap();
        println!("createAccount failed: {:?}", body);
        panic!("createAccount returned non-200");
    }
    assert_eq!(res.status(), StatusCode::OK);
    let create_body: Value = res.json().await.expect("Not JSON");
    assert_eq!(create_body["did"], did);
    let _jwt = verify_new_account(&client, &did).await;
    /*
    let profile_payload = json!({
        "repo": did,
        "collection": "app.bsky.actor.profile",
        "rkey": "self",
        "record": {
            "$type": "app.bsky.actor.profile",
            "displayName": "DID Web User",
            "description": "Testing lifecycle"
        }
    });
    let res = client.post(format!("{}/xrpc/com.atproto.repo.putRecord", base_url().await))
        .bearer_auth(_jwt)
        .json(&profile_payload)
        .send()
        .await
        .expect("Failed putRecord");
    if res.status() != StatusCode::OK {
        let body: Value = res.json().await.unwrap();
        println!("putRecord failed: {:?}", body);
        panic!("putRecord returned non-200");
    }
    assert_eq!(res.status(), StatusCode::OK);
    let res = client.get(format!("{}/xrpc/com.atproto.repo.getRecord", base_url().await))
        .query(&[
            ("repo", &handle),
            ("collection", &"app.bsky.actor.profile".to_string()),
            ("rkey", &"self".to_string())
        ])
        .send()
        .await
        .expect("Failed getRecord");
    if res.status() != StatusCode::OK {
        let body: Value = res.json().await.unwrap();
        println!("getRecord failed: {:?}", body);
        panic!("getRecord returned non-200");
    }
    let record_body: Value = res.json().await.expect("Not JSON");
    assert_eq!(record_body["value"]["displayName"], "DID Web User");
    */
}

#[tokio::test]
async fn test_get_recommended_did_credentials_success() {
    let client = client();
    let (access_jwt, _) = create_account_and_login(&client).await;
    let res = client
        .get(format!(
            "{}/xrpc/com.atproto.identity.getRecommendedDidCredentials",
            base_url().await
        ))
        .bearer_auth(&access_jwt)
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::OK);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert!(body["rotationKeys"].is_array());
    assert!(body["alsoKnownAs"].is_array());
    assert!(body["verificationMethods"].is_object());
    assert!(body["services"].is_object());
    let rotation_keys = body["rotationKeys"].as_array().unwrap();
    assert!(!rotation_keys.is_empty());
    assert!(rotation_keys[0].as_str().unwrap().starts_with("did:key:"));
    let also_known_as = body["alsoKnownAs"].as_array().unwrap();
    assert!(!also_known_as.is_empty());
    assert!(also_known_as[0].as_str().unwrap().starts_with("at://"));
    assert!(body["verificationMethods"]["atproto"].is_string());
    assert_eq!(
        body["services"]["atproto_pds"]["type"],
        "AtprotoPersonalDataServer"
    );
    assert!(body["services"]["atproto_pds"]["endpoint"].is_string());
}

#[tokio::test]
async fn test_get_recommended_did_credentials_no_auth() {
    let client = client();
    let res = client
        .get(format!(
            "{}/xrpc/com.atproto.identity.getRecommendedDidCredentials",
            base_url().await
        ))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "AuthenticationRequired");
}

#[tokio::test]
async fn test_update_handle_to_same() {
    let client = client();
    let (access_jwt, _did) = create_account_and_login(&client).await;
    let session = client
        .get(format!(
            "{}/xrpc/com.atproto.server.getSession",
            base_url().await
        ))
        .bearer_auth(&access_jwt)
        .send()
        .await
        .expect("Failed to get session");
    let session_body: Value = session.json().await.expect("Invalid JSON");
    let current_handle = session_body["handle"]
        .as_str()
        .expect("No handle")
        .to_string();
    let short_handle = current_handle.split('.').next().unwrap_or(&current_handle);
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .bearer_auth(&access_jwt)
        .json(&json!({ "handle": short_handle }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::OK);
}

#[tokio::test]
async fn test_update_handle_no_auth() {
    let client = client();
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .json(&json!({ "handle": "newhandle" }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "AuthenticationRequired");
}

#[tokio::test]
async fn test_update_handle_invalid_characters() {
    let client = client();
    let (access_jwt, _did) = create_account_and_login(&client).await;
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .bearer_auth(&access_jwt)
        .json(&json!({ "handle": "invalid@handle!" }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "InvalidHandle");
}

#[tokio::test]
async fn test_update_handle_empty() {
    let client = client();
    let (access_jwt, _did) = create_account_and_login(&client).await;
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .bearer_auth(&access_jwt)
        .json(&json!({ "handle": "" }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "InvalidRequest");
}

#[tokio::test]
async fn test_update_handle_taken() {
    let client = client();
    let (access_jwt1, _did1) = create_account_and_login(&client).await;
    let (access_jwt2, _did2) = create_account_and_login(&client).await;
    let short_handle = format!("taken{}", &uuid::Uuid::new_v4().to_string()[..8]);
    let update1 = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .bearer_auth(&access_jwt1)
        .json(&json!({ "handle": short_handle }))
        .send()
        .await
        .expect("Failed to update handle");
    assert_eq!(update1.status(), StatusCode::OK);
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .bearer_auth(&access_jwt2)
        .json(&json!({ "handle": short_handle }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "HandleTaken");
}

#[tokio::test]
async fn test_update_handle_too_short() {
    let client = client();
    let (access_jwt, _did) = create_account_and_login(&client).await;
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .bearer_auth(&access_jwt)
        .json(&json!({ "handle": "ab" }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "InvalidHandle");
    assert!(body["message"].as_str().unwrap().contains("short"));
}

#[tokio::test]
async fn test_update_handle_too_long() {
    let client = client();
    let (access_jwt, _did) = create_account_and_login(&client).await;
    let res = client
        .post(format!(
            "{}/xrpc/com.atproto.identity.updateHandle",
            base_url().await
        ))
        .bearer_auth(&access_jwt)
        .json(&json!({ "handle": "thishandleiswaytoolongforservicedomain" }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "InvalidHandle");
    assert!(body["message"].as_str().unwrap().contains("long"));
}

#[tokio::test]
async fn test_verify_handle_ownership_invalid_did() {
    let client = client();
    let res = client
        .post(format!(
            "{}/xrpc/_identity.verifyHandleOwnership",
            base_url().await
        ))
        .json(&json!({
            "handle": "some.handle.test",
            "did": "not-a-did"
        }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}

#[tokio::test]
async fn test_verify_handle_ownership_invalid_handle() {
    let client = client();
    let res = client
        .post(format!(
            "{}/xrpc/_identity.verifyHandleOwnership",
            base_url().await
        ))
        .json(&json!({
            "handle": "@#$!",
            "did": "did:plc:abc123"
        }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["error"], "InvalidHandle");
}

#[tokio::test]
async fn test_verify_handle_ownership_missing_fields() {
    let client = client();
    let res = client
        .post(format!(
            "{}/xrpc/_identity.verifyHandleOwnership",
            base_url().await
        ))
        .json(&json!({}))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}

#[tokio::test]
async fn test_verify_handle_ownership_unresolvable() {
    let client = client();
    let res = client
        .post(format!(
            "{}/xrpc/_identity.verifyHandleOwnership",
            base_url().await
        ))
        .json(&json!({
            "handle": "nonexistent.example.com",
            "did": "did:plc:abc123def456"
        }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::OK);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["verified"], false);
    assert!(body["error"].as_str().is_some());
}

#[tokio::test]
async fn test_verify_handle_ownership_wrong_did() {
    let client = client();
    let res = client
        .post(format!(
            "{}/xrpc/_identity.verifyHandleOwnership",
            base_url().await
        ))
        .json(&json!({
            "handle": "nonexistent.example.com",
            "did": "did:plc:aaaaaaaaaaaaaaaaaaaaaa"
        }))
        .send()
        .await
        .expect("Failed to send request");
    assert_eq!(res.status(), StatusCode::OK);
    let body: Value = res.json().await.expect("Response was not valid JSON");
    assert_eq!(body["verified"], false);
    assert!(body["error"].as_str().is_some());
    assert!(body["method"].is_null());
}
