use json_eval_rs::jsoneval::cancellation::CancellationToken;
use json_eval_rs::JSONEval;

#[test]
fn test_evaluate_pre_cancelled() {
    let schema = r#"{
        "type": "object",
        "properties": {
            "a": { "type": "string", "rules": [{ "value": "test" }] }
        }
    }"#;
    let mut eval = JSONEval::new(schema, None, None).unwrap();
    let token = CancellationToken::new();
    token.cancel();

    let result = eval.evaluate("{}", None, None, Some(&token));
    assert_eq!(result, Err("Cancelled".to_string()));
}

#[test]
fn test_evaluate_dependents_pre_cancelled() {
    let schema = r#"{
        "type": "object",
        "properties": {
            "a": { "type": "string" },
            "b": { "type": "string", "rules": [{ "$if": "a == 'foo'", "value": "bar" }] }
        }
    }"#;
    let mut eval = JSONEval::new(schema, None, None).unwrap();
    let token = CancellationToken::new();
    token.cancel();

    let result = eval.evaluate_dependents(
        &vec!["a".to_string()],
        Some(r#"{"a": "foo"}"#),
        None,
        false,
        Some(&token),
        None,
        true,
    );
    assert_eq!(result, Err("Cancelled".to_string()));
}

#[test]
fn test_validate_pre_cancelled() {
    let schema = r#"{
        "type": "object",
        "properties": {
            "a": { "type": "string", "minLength": 5 }
        }
    }"#;
    let mut eval = JSONEval::new(schema, None, None).unwrap();
    let token = CancellationToken::new();
    token.cancel();

    let result = eval.validate(r#"{"a": "short"}"#, None, None, Some(&token));
    assert_eq!(result, Err("Cancelled".to_string()));
}

#[test]
fn test_cancellation_mid_evaluation() {
    // This test attempts to cancel a "heavy" operation.
    // Since we don't have a truly heavy operation in this small schema, we simulate it
    // by using a table with many rows if possible, or just checking if we can cancel
    // at checkpoints.

    // We'll create a token, spawn a thread to cancel it immediately,
    // and run evaluation. It might finish before cancellation if it's too fast,
    // so this test is a bit flaky if we rely on timing.
    // Ideally we would mock something that sleeps.
    // However, if we loop enough times in a table, we should catch it.

    // Let's rely on manual inspection for deep cancellation or assume pre-cancelled checks prove wiring.
    // But let's try a table evaluation cancellation.

    let schema = r#"{
        "type": "object",
        "properties": {
            "t1": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "c1": { "type": "string", "rules": [{ "value": "val" }] }
                    }
                },
                "x-table": {
                    "config": {
                        "rows": { "$data": "rows" }
                    }
                }
            }
        }
    }"#;

    // Create data with many rows to ensure it takes some time
    let mut rows = Vec::new();
    for i in 0..10 {
        rows.push(serde_json::json!({ "id": i }));
    }
    let data = serde_json::json!({ "rows": rows }).to_string();

    let mut eval = JSONEval::new(schema, None, None).unwrap();
    let token = CancellationToken::new();
    token.cancel(); // Cancel immediately to ensure it catches at start of evaluate_table

    let result = eval.evaluate(&data, None, None, Some(&token));
    assert_eq!(result, Err("Cancelled".to_string()));
}
