use ryl::config::{Overrides, discover_config};
use std::fs;
use tempfile::tempdir;

#[test]
fn ignore_block_supports_negation() {
    let td = tempdir().unwrap();
    let cfg = td.path().join("conf.yaml");
    fs::write(
        &cfg,
        "ignore: |\n  **/*.skip.yaml\n  !**/keep.skip.yaml\n  nested/\nyaml-files: []\nrules: {}\n",
    )
    .unwrap();

    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: Some(cfg.clone()),
            config_data: None,
        },
    )
    .expect("config parse");
    let base = ctx.base_dir.clone();

    assert!(
        ctx.config
            .is_file_ignored(&td.path().join("a.skip.yaml"), &base)
    );
    assert!(
        !ctx.config
            .is_file_ignored(&td.path().join("keep.skip.yaml"), &base)
    );
    assert!(
        ctx.config
            .is_file_ignored(&td.path().join("nested/file.yaml"), &base)
    );
}

#[test]
fn ignore_from_file_patterns_are_loaded() {
    let td = tempdir().unwrap();
    let cfg = td.path().join("conf.yaml");
    let ignore = td.path().join(".yamllint-ignore");
    fs::write(
        &cfg,
        "ignore-from-file: .yamllint-ignore\nyaml-files: []\nrules: {}\n",
    )
    .unwrap();
    fs::write(&ignore, "*.gen.yaml\n!.keep.yaml\n").unwrap();

    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: Some(cfg.clone()),
            config_data: None,
        },
    )
    .expect("config parse");
    let base = ctx.base_dir.clone();

    assert!(
        ctx.config
            .is_file_ignored(&td.path().join("output.gen.yaml"), &base)
    );
    assert!(
        !ctx.config
            .is_file_ignored(&td.path().join(".keep.yaml"), &base)
    );
    assert!(
        ctx.config
            .ignore_patterns()
            .iter()
            .any(|p| p == "*.gen.yaml")
    );
}

#[test]
fn ignore_directory_pattern_allows_reinclude_of_specific_file() {
    let td = tempdir().unwrap();
    let cfg = td.path().join("conf.yaml");
    fs::write(
        &cfg,
        "ignore: |\n  build/\n  !build/keep.yaml\nyaml-files: []\nrules: {}\n",
    )
    .unwrap();

    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: Some(cfg.clone()),
            config_data: None,
        },
    )
    .expect("config parse");
    let base = ctx.base_dir.clone();

    assert!(
        ctx.config
            .is_file_ignored(&td.path().join("build/drop.yaml"), &base)
    );
    assert!(
        !ctx.config
            .is_file_ignored(&td.path().join("build/keep.yaml"), &base)
    );
}

#[test]
fn ignore_from_file_directory_pattern_allows_reinclude_of_specific_file() {
    let td = tempdir().unwrap();
    let cfg = td.path().join("conf.yaml");
    let ignore = td.path().join(".yamllint-ignore");
    fs::write(
        &cfg,
        "ignore-from-file: .yamllint-ignore\nyaml-files: []\nrules: {}\n",
    )
    .unwrap();
    fs::write(&ignore, "build/\n!build/keep.yaml\n").unwrap();

    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: Some(cfg.clone()),
            config_data: None,
        },
    )
    .expect("config parse");
    let base = ctx.base_dir.clone();

    assert!(
        ctx.config
            .is_file_ignored(&td.path().join("build/drop.yaml"), &base)
    );
    assert!(
        !ctx.config
            .is_file_ignored(&td.path().join("build/keep.yaml"), &base)
    );
}

#[test]
fn rule_ignore_handles_direct_ignore_whitelist_and_parent_fallback() {
    let td = tempdir().unwrap();
    let cfg = td.path().join("conf.yaml");
    fs::write(
        &cfg,
        "yaml-files: []\nrules:\n  trailing-spaces:\n    ignore: |\n      build/\n      !build/keep.yaml\n      drop.yaml\n",
    )
    .unwrap();

    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: Some(cfg.clone()),
            config_data: None,
        },
    )
    .expect("config parse");
    let base = ctx.base_dir.clone();

    assert!(!ctx.config.is_rule_ignored(
        "trailing-spaces",
        &td.path().join("build/keep.yaml"),
        &base
    ));
    assert!(ctx.config.is_rule_ignored(
        "trailing-spaces",
        &td.path().join("drop.yaml"),
        &base
    ));
    assert!(ctx.config.is_rule_ignored(
        "trailing-spaces",
        &td.path().join("build/sub/nested.yaml"),
        &base,
    ));
}

#[test]
fn typed_yaml_surface_handles_extends_and_known_top_level_fields() {
    let td = tempdir().unwrap();
    let base_cfg = td.path().join("base.yaml");
    let child_cfg = td.path().join("child.yaml");
    let ignore_file = td.path().join(".typed-ignore");
    fs::write(
        &base_cfg,
        "ignore: ['base.yaml']\nyaml-files: []\nrules: {}\n",
    )
    .unwrap();
    fs::write(&ignore_file, "child.yaml\n").unwrap();
    fs::write(
        &child_cfg,
        "extends: base.yaml\nignore-from-file: .typed-ignore\nyaml-files: ['*.yaml']\nlocale: en_US.UTF-8\nrules:\n  comments:\n    level: warning\n    require-starting-space: true\n",
    )
    .unwrap();

    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: Some(child_cfg),
            config_data: None,
        },
    )
    .expect("config parse");
    let base = ctx.base_dir.clone();

    assert_eq!(ctx.config.locale(), Some("en_US.UTF-8"));
    assert_eq!(
        ctx.config.rule_level("comments"),
        Some(ryl::config::RuleLevel::Warning)
    );
    assert!(
        ctx.config
            .rule_option_bool("comments", "require-starting-space", false)
    );
    assert!(
        ctx.config
            .is_file_ignored(&td.path().join("child.yaml"), &base)
    );
    assert!(
        !ctx.config
            .is_file_ignored(&td.path().join("base.yaml"), &base),
        "child ignore-from-file should replace parent ignore patterns"
    );
}

#[test]
fn extends_resolves_relative_file_paths() {
    let td = tempdir().unwrap();
    let base_cfg = td.path().join("base.yaml");
    fs::write(
        &base_cfg,
        "ignore: ['base/**']\nyaml-files: []\nrules: {}\n",
    )
    .unwrap();
    let child_cfg = td.path().join("child.yaml");
    fs::write(
        &child_cfg,
        "extends: base.yaml\nignore: ['child/**']\nyaml-files: []\nrules: {}\n",
    )
    .unwrap();

    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: Some(child_cfg.clone()),
            config_data: None,
        },
    )
    .expect("config parse");
    let base = ctx.base_dir.clone();

    assert!(
        !ctx.config
            .is_file_ignored(&td.path().join("base/one.yaml"), &base),
        "parent ignore entries should be replaced by child overrides"
    );
    assert!(
        ctx.config
            .is_file_ignored(&td.path().join("child/two.yaml"), &base)
    );
}

#[test]
fn config_data_builtin_name_expands() {
    let ctx = discover_config(
        &[],
        &Overrides {
            config_file: None,
            config_data: Some("extends: relaxed".to_string()),
        },
    )
    .expect("config parse");

    assert!(ctx.config.rule_names().iter().any(|r| r == "braces"));
}
