use crate::common::TestServer;
use serde_json::json;

/// Helper: insert a finding directly via MongoDB for testing query endpoints.
async fn insert_finding(server: &TestServer, repo_id: &str, title: &str, severity: &str) {
    // We insert via the agent's DB by posting to the internal test path.
    // Since there's no direct "create finding" API, we use MongoDB directly.
    let mongodb_uri = std::env::var("TEST_MONGODB_URI")
        .unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into());

    // Extract the database name from the server's unique DB
    // We'll use the agent's internal DB through the stats endpoint to verify
    let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap();

    // Get the DB name from the test server by parsing the health response
    // For now, we use a direct insert approach
    let db = client.database(&server.db_name());

    let now = mongodb::bson::DateTime::now();
    db.collection::<mongodb::bson::Document>("findings")
        .insert_one(mongodb::bson::doc! {
            "repo_id": repo_id,
            "fingerprint": format!("fp-{title}-{severity}"),
            "scanner": "test-scanner",
            "scan_type": "sast",
            "title": title,
            "description": format!("Test finding: {title}"),
            "severity": severity,
            "status": "open",
            "created_at": now,
            "updated_at": now,
        })
        .await
        .unwrap();
}

#[tokio::test]
async fn list_findings_empty() {
    let server = TestServer::start().await;

    let resp = server.get("/api/v1/findings").await;
    assert_eq!(resp.status(), 200);

    let body: serde_json::Value = resp.json().await.unwrap();
    assert_eq!(body["data"].as_array().unwrap().len(), 0);
    assert_eq!(body["total"], 0);

    server.cleanup().await;
}

#[tokio::test]
async fn list_findings_with_data() {
    let server = TestServer::start().await;

    insert_finding(&server, "repo1", "SQL Injection", "critical").await;
    insert_finding(&server, "repo1", "XSS", "high").await;
    insert_finding(&server, "repo2", "Info Leak", "low").await;

    let resp = server.get("/api/v1/findings").await;
    assert_eq!(resp.status(), 200);
    let body: serde_json::Value = resp.json().await.unwrap();
    assert_eq!(body["total"], 3);

    // Filter by severity
    let resp = server.get("/api/v1/findings?severity=critical").await;
    let body: serde_json::Value = resp.json().await.unwrap();
    assert_eq!(body["total"], 1);
    assert_eq!(body["data"][0]["title"], "SQL Injection");

    // Filter by repo
    let resp = server.get("/api/v1/findings?repo_id=repo1").await;
    let body: serde_json::Value = resp.json().await.unwrap();
    assert_eq!(body["total"], 2);

    server.cleanup().await;
}

#[tokio::test]
async fn update_finding_status() {
    let server = TestServer::start().await;

    insert_finding(&server, "repo1", "Test Bug", "medium").await;

    // Get the finding ID
    let resp = server.get("/api/v1/findings").await;
    let body: serde_json::Value = resp.json().await.unwrap();
    let finding_id = body["data"][0]["_id"]["$oid"].as_str().unwrap();

    // Update status to resolved
    let resp = server
        .patch(
            &format!("/api/v1/findings/{finding_id}/status"),
            &json!({ "status": "resolved" }),
        )
        .await;
    assert_eq!(resp.status(), 200);

    // Verify it's updated
    let resp = server.get(&format!("/api/v1/findings/{finding_id}")).await;
    assert_eq!(resp.status(), 200);
    let body: serde_json::Value = resp.json().await.unwrap();
    assert_eq!(body["data"]["status"], "resolved");

    server.cleanup().await;
}

#[tokio::test]
async fn bulk_update_finding_status() {
    let server = TestServer::start().await;

    insert_finding(&server, "repo1", "Bug A", "high").await;
    insert_finding(&server, "repo1", "Bug B", "high").await;

    // Get both finding IDs
    let resp = server.get("/api/v1/findings").await;
    let body: serde_json::Value = resp.json().await.unwrap();
    let ids: Vec<String> = body["data"]
        .as_array()
        .unwrap()
        .iter()
        .map(|f| f["_id"]["$oid"].as_str().unwrap().to_string())
        .collect();

    // Bulk update
    let resp = server
        .patch(
            "/api/v1/findings/bulk-status",
            &json!({
                "ids": ids,
                "status": "false_positive"
            }),
        )
        .await;
    assert_eq!(resp.status(), 200);

    // Verify both are updated
    for id in &ids {
        let resp = server.get(&format!("/api/v1/findings/{id}")).await;
        let body: serde_json::Value = resp.json().await.unwrap();
        assert_eq!(body["data"]["status"], "false_positive");
    }

    server.cleanup().await;
}
