=head1 COPYRIGHT AND LICENSE

Copyright (C) 2026 Xiyue Deng <manphiz@gmail.com>

This program is free software: you can redistribute it and/or modify it under
the terms of the SPDX-License-Identifier: GPL-2.0-or-later

=cut

package TestHelper;

use strict;
use warnings;
use Exporter qw(import);

use File::Copy::Recursive qw(dircopy);
use File::chdir;
use Path::Tiny;

use Debian::Debhelper::Dh_Lib;

our @EXPORT = qw(
    setup_temp_work_dir
    run_dh_make_elpa_in_temp_work_dir
    get_all_test_cases
    is_team_package
    check_no_diff
    update_expected_output
);

sub setup_temp_work_dir {
    my ($test_data_dir, $tmp_dir) = @_;

    my $source = path($test_data_dir)->child("repo");
    my $work_dir = $tmp_dir->child($source->basename);

    dircopy($source->stringify, $work_dir->stringify)
        or die "Failed to copy from $source to $work_dir: $!";

    # We cannot add a real git repository nested under t/data into the parent
    # project.  This hack checks whether a `git' directory exists in the test
    # case repo, and renames it to `.git' when running `dh-make-elpa'.
    my $repo_git_dir = $work_dir->child("git");
    my $repo_dot_git_dir = $work_dir->child(".git");
    rename($repo_git_dir, $repo_dot_git_dir) if -d $repo_git_dir->stringify;
    # Also create these empty directory that cannot be checked in.
    $repo_dot_git_dir->child("refs")->mkpath;
    $repo_dot_git_dir->child("objects")->mkpath;

    return $work_dir;
}

sub run_dh_make_elpa_in_temp_work_dir {
    my ($work_dir, $pkg_emacsen, $project_root,
        $use_dh_make_elpa_in_source) = @_;

    local $CWD = $work_dir;

    $ENV{DEBFULLNAME} = "DhMakeElpa";
    $ENV{DEBEMAIL} = "dh-make-elpa\@test.debian";

    my @cmd;

    if ($use_dh_make_elpa_in_source) {
        # Set up templates in with $HOME created under /tmp as sbuild will set
        # $HOME to "/sbuild-nonexistent" by default.
        $ENV{HOME} = "/tmp/dh-make-elpa-test-home";
        path($ENV{HOME})->mkpath;
        my $test_data_dir = path($ENV{HOME})->child(".dh-make-elpa");
        $test_data_dir->mkpath;
        my $template_source_dir = path($project_root)->child("share");
        foreach my $template_file ($template_source_dir->children) {
            if ($template_file->is_file) {
                $template_file->copy($test_data_dir->child(
                                         $template_file->basename));
            }
        }

        $ENV{PERL5LIB} = path($project_root)->child("lib")->stringify;

        # Use dh-make-elpa in the source tree.
        push(@cmd, path($project_root)->child("dh-make-elpa")->stringify);
    } else {
        # Use dh-make-elpa from installed package.  Usually done in autopkgtest.
        push(@cmd, "/usr/bin/dh-make-elpa");
    }
    # print_and_doit will cause error when there is only one command parameter.
    # So adding a `--verbose' to ensure at least 2 command parts.
    push(@cmd, "--verbose");
    push(@cmd, "--test-mode");
    push(@cmd, "--pkg-emacsen") if $pkg_emacsen;

    print_and_doit(@cmd);
}

sub get_all_test_cases {
    my $cwd = Path::Tiny->cwd;
    my $test_data_root_dir = $cwd->child("t", "data");

    my @test_cases = sub {
        my ($test_data_root_dir) = @_;

        my $iter = $test_data_root_dir->iterator({ recurse => 0 });
        my @subdirs;
        while (my $subdir = $iter->()) {
            push(@subdirs, $subdir) if -d $subdir;
        }

        return @subdirs;
    }->($test_data_root_dir);

    return @test_cases;
}

sub is_team_package {
    my ($test_case) = @_;
    return ($test_case->stringify =~ /_team\z/) ? 1 : 0;
}

sub check_no_diff {
    my ($test_case, $work_dir) = @_;

    my $expected = $test_case->child("expected", "debian")->stringify;
    my $actual = $work_dir->child("debian")->stringify;

    # Ignore lines of maintainer update timestamp, which changes each time it
    # runs.
    my $diff_output =
        qx(/usr/bin/diff -urN $expected $actual);
    my $test_case_name = $test_case->basename;
    if (length($diff_output) != 0) {
        my $message = <<"EOF";
Found diff in test case \"$test_case_name\":
==========
$diff_output
==========
EOF
        print $message;
    }

    return length($diff_output) == 0;
}

sub update_expected_output {
    my ($test_case, $work_dir) = @_;

    my $expected_path = $test_case->child("expected", "debian");
    my $actual_path = $work_dir->child("debian");

    $expected_path->remove_tree();
    dircopy($actual_path->stringify, $expected_path->stringify)
        or die "Failed to copy from $actual_path to $expected_path";
}

1;
