mod support;

use self::support::{
    assert_revision_tree_matches_git, make_executable, with_mounted_timefs, write_symlink,
    RepositoryFixture,
};
use std::fs;

#[test]
fn differential_revision_matrix_matches_git() {
    let fixture = differential_fixture();
    let parent_sha = fixture.git_rev_parse("HEAD~1");
    let parent_short = fixture.git_rev_parse_short("HEAD~1");

    let (_value, mount) = with_mounted_timefs(fixture.path(), &[], &["-f"], |mounted| {
        let cases = vec![
            (mounted.path().join("now"), "HEAD"),
            (mounted.path().join("at").join("HEAD"), "HEAD"),
            (mounted.path().join("at").join("HEAD~1"), "HEAD~1"),
            (mounted.path().join("at").join("topic"), "topic"),
            (mounted.path().join("at").join("v1.0"), "v1.0"),
            (
                mounted.path().join("at").join(parent_sha.as_str()),
                "HEAD~1",
            ),
            (
                mounted.path().join("commits").join(parent_short.as_str()),
                "HEAD~1",
            ),
            (
                mounted
                    .path()
                    .join("refs")
                    .join("heads")
                    .join("feature")
                    .join("x"),
                "feature/x",
            ),
        ];

        for (mount_root, rev) in cases {
            assert_revision_tree_matches_git(&fixture, &mount_root, rev);
        }

        assert_eq!(
            fs::read(mounted.path().join("now").join("README.md"))
                .expect("HEAD README should be readable"),
            fixture.git_show_path("HEAD", b"README.md")
        );
        assert_eq!(
            fs::read(mounted.path().join("at").join("HEAD~1").join("topic.txt"),)
                .expect("HEAD~1 topic marker should be readable"),
            fixture.git_show_path("HEAD~1", b"topic.txt")
        );
        assert_eq!(
            fs::read_link(
                mounted
                    .path()
                    .join("at")
                    .join("v1.0")
                    .join("link-to-script")
            )
            .expect("tagged symlink should resolve")
            .as_os_str()
            .as_encoded_bytes(),
            fixture.git_show_path("v1.0", b"link-to-script")
        );
        assert_eq!(
            fs::read(
                mounted
                    .path()
                    .join("refs")
                    .join("heads")
                    .join("feature")
                    .join("x")
                    .join("topic.txt"),
            )
            .expect("feature/x topic marker should be readable"),
            fixture.git_show_path("feature/x", b"topic.txt")
        );
    });

    assert!(
        mount.stderr.is_empty(),
        "differential mount emitted unexpected stderr: {}",
        mount.stderr
    );
}

fn differential_fixture() -> RepositoryFixture {
    let fixture = RepositoryFixture::new("timefs-task8-differential");
    fs::create_dir_all(fixture.path().join("nested"))
        .expect("fixture directory creation should succeed");

    fs::write(
        fixture.path().join("README.md"),
        b"timefs differential fixture\n",
    )
    .expect("README write should succeed");
    fs::write(
        fixture.path().join("nested/data.bin"),
        [0_u8, 1, 2, 3, 0, 255],
    )
    .expect("binary write should succeed");
    fs::write(
        fixture.path().join("run.sh"),
        b"#!/bin/sh\necho differential\n",
    )
    .expect("script write should succeed");
    make_executable(&fixture.path().join("run.sh"));
    write_symlink("run.sh", &fixture.path().join("link-to-script"));

    fixture.git(["add", "."]);
    fixture.git(["commit", "-m", "Create differential fixture"]);
    fixture.git(["tag", "v1.0"]);

    fs::write(
        fixture.path().join("README.md"),
        b"timefs differential fixture v2\n",
    )
    .expect("updated README write should succeed");
    fs::write(fixture.path().join("topic.txt"), b"branch point data\n")
        .expect("topic marker write should succeed");
    fixture.git(["add", "."]);
    fixture.git(["commit", "-m", "Prepare differential matrix"]);
    fixture.git(["branch", "topic"]);
    fixture.git(["branch", "feature/x"]);

    fs::write(fixture.path().join("head-only.txt"), b"current HEAD\n")
        .expect("head marker write should succeed");
    fixture.git(["add", "."]);
    fixture.git(["commit", "-m", "Advance HEAD"]);

    fixture
}
