#!/usr/bin/env bats

setup() {
    load 'test_helper/common_setup'
    _common_setup
}

teardown() {
    _common_teardown
}

@test "commit-a" {
    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["pre-commit"] {
        steps {
            ["foo"] {
                check = "echo 'foo: {{files}}'"
            }
        }
    }
}
EOF
    mkdir -p src
    touch src/foo.rs
    git add hk.pkl src/foo.rs
    git commit -m "initial commit"
    hk install

    echo "text" > src/foo.rs
    run git commit -am "add text"
    assert_success
    assert_output --partial "foo: src/foo.rs"
}

@test "unstaged changes get restored" {
    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["pre-commit"] {
        steps {
            ["succeed"] { check = "exit 0" }
            ["fail"] { check = "exit 1" }
        }
    }
}
EOF
    mkdir -p src
    touch src/foo.rs
    git add hk.pkl src/foo.rs
    git commit -m "initial commit"
    hk install

    echo "staged" >> src/foo.rs
    git add src/foo.rs
    echo "unstaged" >> src/foo.rs
    run git commit -m "staged changes"
    assert_failure
    run cat src/foo.rs
    assert_output "staged
unstaged"
    run git diff
    assert_output --partial "staged
+unstaged"
}

@test "files_between_refs uses merge base correctly" {
    # Create initial commit
    echo "base content" > base.txt
    git add base.txt
    git commit -m "Initial commit"
    BASE_COMMIT=$(git rev-parse HEAD)

    # Create and switch to feature branch
    git checkout -b feature
    echo "feature content" > feature.txt
    git add feature.txt
    git commit -m "Add feature.txt"
    FEATURE_COMMIT=$(git rev-parse HEAD)

    # Switch back to main and make changes
    git checkout main
    echo "main content" > main.txt
    git add main.txt
    git commit -m "Add main.txt"
    MAIN_COMMIT=$(git rev-parse HEAD)

    # Merge feature branch into main
    git merge feature -m "Merge feature branch"
    MERGE_COMMIT=$(git rev-parse HEAD)

    # Create hk.pkl with a simple step that just prints the files
    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["fix"] {
        steps {
            ["print-files"] {
                check = "echo '{{files}}'"
            }
        }
    }
}
EOF

    # The progress message renders the command with `files` truncated to
    # "first_file …" when there's more than one file — keeps CI log lines
    # bounded for steps matching hundreds of files while still showing one
    # concrete example. The full list still surfaces via streamed stdout.

    # Test files between base and feature
    run hk fix -v --from-ref=$BASE_COMMIT --to-ref=$FEATURE_COMMIT
    assert_success
    assert_output --partial "print-files – 1 file –  – echo 'feature.txt'"
    assert_output --partial "print-files – feature.txt"

    # Test files between base and merge commit
    run hk fix --from-ref=$BASE_COMMIT --to-ref=$MERGE_COMMIT
    assert_success
    assert_output --partial "print-files – 2 files –  – echo 'feature.txt …'"
    assert_output --partial "print-files – feature.txt main.txt"

    # Test files between feature and merge commit
    run hk fix --from-ref=$FEATURE_COMMIT --to-ref=$MERGE_COMMIT
    assert_success
    assert_output --partial "print-files – 1 file –  – echo 'main.txt'"
    assert_output --partial "print-files – main.txt"
}

@test "files_between_refs excludes deleted paths for case-only renames" {
    _disable_test_cache

    mkdir -p src
    echo "content" > src/example.txt
    git add src/example.txt
    git commit -m "add example"
    BASE_COMMIT=$(git rev-parse HEAD)

    git mv src/example.txt src/Example.txt
    git commit -m "rename example"
    TARGET_COMMIT=$(git rev-parse HEAD)

    # On case-sensitive filesystems, emulate the case-insensitive exists() check
    # by recreating the deleted spelling as an untracked path. On case-insensitive
    # filesystems this writes through the old spelling to the tracked target path.
    echo "worktree content" > src/example.txt

    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["check"] {
        steps {
            ["print-files"] {
                check = "echo '{{files}}'"
            }
        }
    }
}
EOF

    for use_libgit2 in 1 0; do
        HK_LIBGIT2="$use_libgit2" run hk check \
            --from-ref="$BASE_COMMIT" \
            --to-ref="$TARGET_COMMIT"
        assert_success
        assert_output --partial "print-files – src/Example.txt"
        refute_output --partial "src/example.txt"
    done
}

@test "files_between_refs falls back to two-dot diff without merge base using libgit2" {
    echo "main content" > main.txt
    git add main.txt
    git commit -m "main commit"
    MAIN_COMMIT=$(git rev-parse HEAD)

    git checkout --orphan unrelated
    git rm -rf .
    echo "unrelated content" > unrelated.txt
    git add unrelated.txt
    git commit -m "unrelated commit"
    UNRELATED_COMMIT=$(git rev-parse HEAD)
    git checkout main

    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["check"] {
        steps {
            ["print-files"] {
                check = "echo '{{files}}'"
            }
        }
    }
}
EOF

    HK_LIBGIT2=1 run hk check --from-ref=$UNRELATED_COMMIT --to-ref=$MAIN_COMMIT
    assert_success
    assert_output --partial "print-files – main.txt"
    refute_output --partial "unrelated.txt"
}

@test "files_between_refs falls back to two-dot diff without merge base using shell git" {
    echo "main content" > main.txt
    git add main.txt
    git commit -m "main commit"
    MAIN_COMMIT=$(git rev-parse HEAD)

    git checkout --orphan unrelated
    git rm -rf .
    echo "unrelated content" > unrelated.txt
    git add unrelated.txt
    git commit -m "unrelated commit"
    UNRELATED_COMMIT=$(git rev-parse HEAD)
    git checkout main

    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["check"] {
        steps {
            ["print-files"] {
                check = "echo '{{files}}'"
            }
        }
    }
}
EOF

    HK_LIBGIT2=0 run hk check --from-ref=$UNRELATED_COMMIT --to-ref=$MAIN_COMMIT
    assert_success
    assert_output --partial "print-files – main.txt"
    refute_output --partial "unrelated.txt"
}

@test "files_between_refs lints all files when from-ref is unresolvable using libgit2" {
    echo "main content" > main.txt
    mkdir -p src
    echo "nested content" > src/nested.txt
    git add main.txt src/nested.txt
    git commit -m "main commit"
    MAIN_COMMIT=$(git rev-parse HEAD)

    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["check"] {
        steps {
            ["print-files"] {
                check = "echo '{{files}}'"
            }
        }
    }
}
EOF

    # An unresolvable from-ref (mirrors the empty-remote first push, where
    # default_branch() yields the literal "origin/HEAD") diffs against the
    # empty tree, so every file at to-ref is linted.
    HK_LIBGIT2=1 run hk check --from-ref=origin/HEAD --to-ref=$MAIN_COMMIT
    assert_success
    assert_output --partial "print-files – main.txt src/nested.txt"
}

@test "files_between_refs lints all files when from-ref is unresolvable using shell git" {
    echo "main content" > main.txt
    mkdir -p src
    echo "nested content" > src/nested.txt
    git add main.txt src/nested.txt
    git commit -m "main commit"
    MAIN_COMMIT=$(git rev-parse HEAD)

    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["check"] {
        steps {
            ["print-files"] {
                check = "echo '{{files}}'"
            }
        }
    }
}
EOF

    # Shell-git fallback: git_rev_exists() returns false for the unresolvable
    # from-ref, so files_between_refs lists every file at to-ref via ls-tree
    # (object-format agnostic, unlike a hard-coded empty-tree hash).
    HK_LIBGIT2=0 run hk check --from-ref=origin/HEAD --to-ref=$MAIN_COMMIT
    assert_success
    assert_output --partial "print-files – main.txt src/nested.txt"
}

@test "files staged for deletion are not included with --all" {
    cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
    ["check"] {
        steps {
            ["foo"] {
                check = "echo 'foo: {{files}}'"
            }
        }
    }
}
EOF
    mkdir -p src
    touch src/foo.rs
    git add hk.pkl src/foo.rs
    git commit -m "initial commit"
    hk install

    # Stage the file for deletion
    git rm src/foo.rs

    # Try to commit with --all
    run hk check --all
    assert_success
    assert_output --partial "foo: hk.pkl"
    # Verify the deleted file is not in the output
    refute_output --partial "src/foo.rs"
}
