use earl::expression::binder::bind_arguments;
use earl::expression::parser::parse_expression;
use earl::template::schema::{ParamSpec, ParamType};

fn params() -> Vec<ParamSpec> {
    vec![
        ParamSpec {
            name: "query".to_string(),
            r#type: ParamType::String,
            required: true,
            default: None,
            description: None,
        },
        ParamSpec {
            name: "per_page".to_string(),
            r#type: ParamType::Integer,
            required: false,
            default: Some(serde_json::json!(20)),
            description: None,
        },
        ParamSpec {
            name: "include".to_string(),
            r#type: ParamType::Boolean,
            required: false,
            default: Some(serde_json::json!(false)),
            description: None,
        },
    ]
}

#[test]
fn binds_positional_and_named_arguments() {
    let expr = parse_expression("github.search_issues(\"hello\", per_page=10)").unwrap();
    let bound = bind_arguments(&expr, &params()).unwrap();

    assert_eq!(bound.get("query").unwrap(), "hello");
    assert_eq!(bound.get("per_page").unwrap(), 10);
    assert_eq!(bound.get("include").unwrap(), false);
}

#[test]
fn fails_on_missing_required_argument() {
    let expr = parse_expression("github.search_issues()").unwrap();
    let err = bind_arguments(&expr, &params()).unwrap_err();
    assert!(
        err.to_string()
            .contains("missing required argument `query`")
    );
}

#[test]
fn fails_on_unknown_argument() {
    let expr = parse_expression("github.search_issues(query=\"x\", unknown=1)").unwrap();
    let err = bind_arguments(&expr, &params()).unwrap_err();
    assert!(err.to_string().contains("unknown argument `unknown`"));
}

#[test]
fn fails_on_too_many_positional_arguments() {
    let expr = parse_expression("github.search_issues(\"a\", 1, true, \"extra\")").unwrap();
    let err = bind_arguments(&expr, &params()).unwrap_err();
    assert!(err.to_string().contains("too many positional arguments"));
}

#[test]
fn fails_on_invalid_argument_type() {
    let expr = parse_expression("github.search_issues(query=\"x\", per_page=\"ten\")").unwrap();
    let err = bind_arguments(&expr, &params()).unwrap_err();
    assert!(
        err.to_string()
            .contains("argument `per_page` has invalid type")
    );
}
