use std::fs;
use std::path::Path;
use std::time::Duration;

use donion::config;
use tempfile::TempDir;

fn write_config(dir: &Path, content: &str) {
    fs::write(dir.join("donion.toml"), content).unwrap();
}

#[test]
fn parses_single_branch_with_all_fields() {
    let tmp = TempDir::new().unwrap();
    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "echo build"
deploy = "echo deploy"
poll_rate = 45
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs.len(), 1);
    assert_eq!(cfgs[0].branch, "main");
    assert_eq!(cfgs[0].build, "echo build");
    assert_eq!(cfgs[0].deploy, "echo deploy");
    assert_eq!(cfgs[0].poll_rate, Duration::from_secs(45));
}

#[test]
fn parses_multiple_branches() {
    let tmp = TempDir::new().unwrap();
    let dir_a = tmp.path().join("a");
    let dir_b = tmp.path().join("b");
    fs::create_dir_all(&dir_a).unwrap();
    fs::create_dir_all(&dir_b).unwrap();

    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "echo build-main"
deploy = "echo deploy-main"
directory = "a"

[[branches.staging]]
build = "echo build-staging"
deploy = "echo deploy-staging"
directory = "b"
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs.len(), 2);

    let main = cfgs.iter().find(|c| c.branch == "main").unwrap();
    let staging = cfgs.iter().find(|c| c.branch == "staging").unwrap();
    assert_eq!(main.build, "echo build-main");
    assert_eq!(staging.build, "echo build-staging");
    assert_ne!(main.directory, staging.directory);
}

#[test]
fn allows_multiple_branches_same_directory() {
    let tmp = TempDir::new().unwrap();
    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "echo build"
deploy = "echo deploy"

[[branches.staging]]
build = "echo build"
deploy = "echo deploy"
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs.len(), 2);
}

#[test]
fn allows_same_branch_same_directory_with_names() {
    let tmp = TempDir::new().unwrap();
    write_config(
        tmp.path(),
        r#"
[[branches.main]]
name = "backend"
build = "echo build-backend"
deploy = "echo deploy-backend"

[[branches.main]]
name = "frontend"
build = "echo build-frontend"
deploy = "echo deploy-frontend"
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs.len(), 2);
}

#[test]
fn errors_on_duplicate_names() {
    let tmp = TempDir::new().unwrap();
    write_config(
        tmp.path(),
        r#"
[[branches.main]]
name = "app"
build = "echo build"
deploy = "echo deploy"

[[branches.staging]]
name = "app"
build = "echo build"
deploy = "echo deploy"
"#,
    );

    let err = config::load(tmp.path()).unwrap_err();
    assert!(
        err.to_string().contains("duplicate name"),
        "expected 'duplicate name' error, got: {}",
        err
    );
}

#[test]
fn errors_on_unnamed_same_branch_same_directory() {
    let tmp = TempDir::new().unwrap();
    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "echo build"
deploy = "echo deploy"

[[branches.main]]
build = "echo other"
deploy = "echo other"
"#,
    );

    let err = config::load(tmp.path()).unwrap_err();
    assert!(
        err.to_string().contains("add a name"),
        "expected 'add a name' error, got: {}",
        err
    );
}

#[test]
fn missing_file_defaults_to_main() {
    let tmp = TempDir::new().unwrap();
    fs::write(
        tmp.path().join("Cargo.toml"),
        "[package]\nname = \"x\"\nversion = \"0.1.0\"\n",
    )
    .unwrap();
    fs::create_dir_all(tmp.path().join("src")).unwrap();
    fs::write(tmp.path().join("src/main.rs"), "fn main() {}").unwrap();

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs.len(), 1);
    assert_eq!(cfgs[0].branch, "main");
    assert_eq!(cfgs[0].build, "cargo build --release");
    assert_eq!(cfgs[0].deploy, "cargo run --release");
}

#[test]
fn missing_file_no_project_errors() {
    let tmp = TempDir::new().unwrap();
    let err = config::load(tmp.path()).unwrap_err();
    assert!(err.to_string().contains("project detection failed"));
}

#[test]
fn errors_on_empty_branches() {
    let tmp = TempDir::new().unwrap();
    write_config(tmp.path(), "[branches]\n");

    let err = config::load(tmp.path()).unwrap_err();
    assert!(err.to_string().contains("no [[branches.*]] entries"));
}

#[test]
fn uses_project_detection_when_commands_missing() {
    let tmp = TempDir::new().unwrap();
    fs::write(
        tmp.path().join("Cargo.toml"),
        "[package]\nname = \"test\"\nversion = \"0.1.0\"\n",
    )
    .unwrap();
    fs::create_dir_all(tmp.path().join("src")).unwrap();
    fs::write(tmp.path().join("src/main.rs"), "fn main() {}").unwrap();

    write_config(
        tmp.path(),
        r#"
[[branches.main]]
poll_rate = 10
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs[0].build, "cargo build --release");
    assert_eq!(cfgs[0].deploy, "cargo run --release");
}

#[test]
fn partial_override_uses_detection_for_missing() {
    let tmp = TempDir::new().unwrap();
    fs::write(
        tmp.path().join("Cargo.toml"),
        "[package]\nname = \"test\"\nversion = \"0.1.0\"\n",
    )
    .unwrap();
    fs::create_dir_all(tmp.path().join("src")).unwrap();
    fs::write(tmp.path().join("src/main.rs"), "fn main() {}").unwrap();

    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "custom-build.sh"
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs[0].build, "custom-build.sh");
    assert_eq!(cfgs[0].deploy, "cargo run --release");
}

#[test]
fn default_poll_rate_is_30s() {
    let tmp = TempDir::new().unwrap();
    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "echo b"
deploy = "echo d"
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs[0].poll_rate, Duration::from_secs(10));
}

#[test]
fn directory_defaults_to_base_dir() {
    let tmp = TempDir::new().unwrap();
    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "echo b"
deploy = "echo d"
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs[0].directory, tmp.path().canonicalize().unwrap());
}

#[test]
fn directory_override_resolves_relative() {
    let tmp = TempDir::new().unwrap();
    let sub = tmp.path().join("subdir");
    fs::create_dir_all(&sub).unwrap();

    write_config(
        tmp.path(),
        r#"
[[branches.main]]
build = "echo build"
deploy = "echo deploy"
directory = "subdir"
"#,
    );

    let cfgs = config::load(tmp.path()).unwrap();
    assert_eq!(cfgs[0].directory, sub.canonicalize().unwrap());
}
