package Devscripts::Salsa::update_gbp_conf;

use strict;
use Devscripts::Output;
use Moo::Role;
use Config::IniFiles;
use MIME::Base64 'decode_base64';
use List::Util 'first';
use Dpkg::IPC;
use File::Temp qw(tempfile);

with 'Devscripts::Salsa::Repo';

sub prompt_yes_no {
    my $prompt = shift;
    my $ans;
    while ( defined($ans = ds_prompt("$prompt [Y/n] "))
        and $ans !~ accept
        and $ans !~ refuse) {
        # no-op
    }
    return $ans =~ accept;
}

sub update_gbp_conf {
    my ($self, $project_name) = @_;

    my @repos = $self->get_repo(0, $project_name);
    return 1 unless (ref $repos[0]);
    my ($id, $str) = @{ $repos[0] };

    if (!$id) {
        ds_warn "Project not found: $project_name\n";
        return 1;
    }

    my $project;
    eval { $project = $self->api->project($id) };
    if ($@ || !$project) {
        ds_warn "Failed to fetch project details for project ID $id: $@\n";
        return 1;
    }

    my $default_branch = $project->{default_branch};
    ds_verbose "Default branch is $default_branch\n";

    my $branches;
    eval { $branches = $self->api->branches($id) };
    if ($@ || !$branches) {
        ds_warn "Failed to fetch branches for project ID $id: $@\n";
        return 1;
    }
    my @branch_names = map { $_->{name} } @$branches;

    my @debian_branch_candidates
      = qw(debian/latest debian/unstable main master);
    my $debian_branch = first {
        my $b = $_;
        grep { $_ eq $b } @branch_names
    } @debian_branch_candidates;
    unless ($debian_branch) {
        ds_warn "Could not find a suitable debian-branch.\n";
        return 1;
    }

    my @upstream_branch_candidates = qw(upstream/latest upstream);
    my $upstream_branch            = first {
        my $b = $_;
        grep { $_ eq $b } @branch_names
    } @upstream_branch_candidates;

    my $pristine_tar_branch;
    if (grep { $_ eq 'pristine-tar' } @branch_names) {
        $pristine_tar_branch = 'pristine-tar';
    }

    my $gbp_conf_path = 'debian/gbp.conf';
    my $file;
    my $file_content = '';
    eval {
        $file
          = $self->api->file($id, $gbp_conf_path, { ref => $default_branch });
        $file_content = decode_base64($file->{content}) if $file;
    };
    if ($@) {
        ds_warn
"Failed to fetch $gbp_conf_path from $default_branch branch, assuming it does not exist: $@\n";
    }

    my $cfg;
    if ($file_content) {
        my ($fh, $filename) = tempfile();
        print $fh $file_content;
        close $fh;
        $cfg = Config::IniFiles->new(-file => $filename);
        unlink $filename;
    } else {
        $cfg = Config::IniFiles->new();
    }

    my $original_content = $file_content;

    $cfg->newval('DEFAULT', 'debian-branch', $debian_branch);
    if ($upstream_branch) {
        $cfg->newval('DEFAULT', 'upstream-branch', $upstream_branch);
    } else {
        $cfg->delval('DEFAULT', 'upstream-branch');
    }
    if ($pristine_tar_branch) {
        $cfg->newval('DEFAULT', 'pristine-tar', 'True');
    } else {
        $cfg->delval('DEFAULT', 'pristine-tar');
    }

    my ($fh, $filename) = tempfile();
    my $success = $cfg->WriteConfig($filename);
    close $fh;

    my $new_content = do {
        local $/;
        open my $fh_read, '<', $filename
          or die "Cannot open temp file $filename: $!";
        <$fh_read>;
    };
    unlink $filename;

    if ($new_content eq $original_content) {
        ds_msg "debian/gbp.conf is already up-to-date.\n";
        return 0;
    }

    my $diff;
    my ($old_fh, $old_fn) = tempfile();
    print $old_fh $original_content;
    close $old_fh;
    my ($new_fh, $new_fn) = tempfile();
    print $new_fh $new_content;
    close $new_fh;

    spawn(
        exec      => ['diff', '-u', $old_fn, $new_fn],
        to_string => \$diff,
        nocheck   => 1,    # Ignore non-zero exit code
    );
    unlink $old_fn;
    unlink $new_fn;

    print $diff;

    if (!$self->config->yes) {
        unless (prompt_yes_no("Commit these changes?")) {
            ds_msg "Aborting.";
            return 0;
        }
    }

    my $commit_message = "Update debian/gbp.conf";

    my @actions;
    push @actions,
      {
        action    => ($original_content ? 'update' : 'create'),
        file_path => $gbp_conf_path,
        content   => $new_content,
      };

    eval {
        $self->api->create_commit(
            $id,
            {
                branch         => $default_branch,
                commit_message => $commit_message,
                actions        => \@actions,
            });
        ds_msg
          "Committed changes to debian/gbp.conf on branch $default_branch\n";
    };
    if ($@) {
        ds_warn "Failed to commit changes: $@\n";
        return 1;
    }

    return 0;
}

1;
