# Parses repo to check if parameters are well set
package Devscripts::Salsa::check_repo;

use strict;
use Devscripts::Output;
use Digest::MD5  qw(md5_hex);
use Digest::file qw(digest_file_hex);
use LWP::UserAgent;
use Moo::Role;

with "Devscripts::Salsa::Repo";

sub check_repo {
    my $self = shift;
    my ($res) = $self->_check_repo(@_);
    return $res;
}

sub _url_md5_hex {
    my $url = shift;
    my $ua  = LWP::UserAgent->new;
    my $res = $ua->get($url, "User-Agent" => "Devscripts/2.22.3",);
    if (!$res->is_success) {
        return undef;
    }
    return Digest::MD5::md5_hex($res->content);
}

sub _check_repo {
    my ($self, @reponames) = @_;
    my $res = 0;
    my @fail;
    unless (@reponames or $self->config->all or $self->config->all_archived) {
        ds_warn "Usage $0 check_repo <--all|--all-archived|names>";
        return 1;
    }
    if (@reponames and $self->config->all) {
        ds_warn "--all with a reponame makes no sense";
        return 1;
    }
    if (@reponames and $self->config->all_archived) {
        ds_warn "--all-archived with a reponame makes no sense";
        return 1;
    }
    # Get repo list from Devscripts::Salsa::Repo
    my @repos = $self->get_repo(0, @reponames);
    return @repos unless (ref $repos[0]);
    foreach my $repo (@repos) {
        my @err;
        my ($id, $name) = @$repo;
        my $project = eval { $self->api->project($id) };
        unless ($project) {
            ds_debug $@;
            ds_warn "Project $name not found";
            next;
        }
        ds_debug "Checking $name ($id)";
        # check description
        my %prms           = $self->desc($name);
        my %prms_multipart = $self->desc_multipart($name);
        if ($self->config->desc) {
            $project->{description} //= '';
            push @err, "bad description: $project->{description}"
              if ($prms{description} ne $project->{description});
        }
        # check build timeout
        if ($self->config->desc) {
            $project->{build_timeout} //= '';
            push @err, "bad build_timeout: $project->{build_timeout}"
              if ($prms{build_timeout} ne $project->{build_timeout});
        }
        # check features (w/permission) & ci config
        foreach (qw(
            analytics_access_level
            auto_devops_enabled
            builds_access_level
            ci_config_path
            container_registry_access_level
            environments_access_level
            feature_flags_access_level
            forking_access_level
            infrastructure_access_level
            issues_access_level
            lfs_enabled
            merge_requests_access_level
            monitor_access_level
            packages_enabled
            pages_access_level
            releases_access_level
            remove_source_branch_after_merge
            repository_access_level
            request_access_enabled
            requirements_access_level
            security_and_compliance_access_level
            service_desk_enabled
            snippets_access_level
            wiki_access_level
            )
        ) {
            my $helptext = '';
            $helptext = ' (enabled)'
              if (defined $prms{$_} and $prms{$_} eq 1);
            $helptext = ' (disabled)'
              if (defined $prms{$_} and $prms{$_} eq 0);
            push @err, "$_ should be $prms{$_}$helptext"
              if (defined $prms{$_}
                and (!defined($project->{$_}) or $project->{$_} ne $prms{$_}));
        }
        # only public projects are accepted
        push @err, "Project visibility: $project->{visibility}"
          unless ($project->{visibility} eq "public");
        # Default branch
        if ($self->config->rename_head) {
            push @err, "Default branch: $project->{default_branch}"
              if ($project->{default_branch} ne $self->config->dest_branch);
        }
        # Webhooks (from Devscripts::Salsa::Hooks)
        my $hooks = $self->enabled_hooks($id);
        unless (defined $hooks) {
            ds_warn "Unable to get $name hooks";
            next;
        }
        # check avatar's path
        if ($self->config->avatar_path) {
            my ($md5_file, $md5_url) = "";
            if ($prms_multipart{avatar}) {
                ds_verbose "Calculating local avatar checksum";
                $md5_file = digest_file_hex($prms_multipart{avatar}, "MD5")
                  or die "$prms_multipart{avatar} failed md5: $!";
                if (    $project->{avatar_url}
                    and $project->{visibility} eq "public") {
                    ds_verbose "Calculating remote avatar checksum";
                    $md5_url = _url_md5_hex($project->{avatar_url})
                      or die "$project->{avatar_url} failed md5: $!";
                    # Will always force avatar if it can't detect
                } elsif ($project->{avatar_url}) {
                    ds_warn
"$name has an avatar, but is set to $project->{visibility} project visibility thus unable to remotely check checksum";
                }
                push @err, "Will set the avatar to be: $prms_multipart{avatar}"
                  if (not length $md5_url or $md5_file ne $md5_url);
            }
        }
        # KGB
        if ($self->config->kgb and not $hooks->{kgb}) {
            push @err, "kgb missing";
        } elsif ($self->config->disable_kgb and $hooks->{kgb}) {
            push @err, "kgb enabled";
        } elsif ($self->config->kgb) {
            push @err,
              "bad irc channel: "
              . substr($hooks->{kgb}->{url},
                length($self->config->kgb_server_url))
              if $hooks->{kgb}->{url} ne $self->config->kgb_server_url
              . $self->config->irc_channel->[0];
            my @wopts = @{ $self->config->kgb_options };
            my @gopts = sort @{ $hooks->{kgb}->{options} };
            my $i     = 0;
            while (@gopts and @wopts) {
                my $a;
                $a = ($wopts[0] cmp $gopts[0]);
                if ($a == -1) {
                    push @err, "Missing KGB option " . shift(@wopts);
                } elsif ($a == 1) {
                    push @err, 'Unwanted KGB option ' . shift(@gopts);
                } else {
                    shift @wopts;
                    shift @gopts;
                }
            }
            push @err, map { "Missing KGB option $_" } @wopts;
            push @err, map { "Unwanted KGB option $_" } @gopts;
        }
        # Email-on-push
        if ($self->config->email
            and not($hooks->{email} and %{ $hooks->{email} })) {
            push @err, "email-on-push missing";
        } elsif (
            $self->config->email
            and $hooks->{email}->{recipients} ne join(
                ' ',
                map {
                    my $a = $_;
                    my $b = $name;
                    $b =~ s#.*/##;
                    $a =~ s/%p/$b/;
                    $a
                } @{ $self->config->email_recipient })
        ) {
            push @err, "bad email recipients " . $hooks->{email}->{recipients};
        } elsif ($self->config->disable_email and $hooks->{kgb}) {
            push @err, "email-on-push enabled";
        }
        # Irker
        if ($self->config->irker and not $hooks->{irker}) {
            push @err, "irker missing";
        } elsif ($self->config->irker
            and $hooks->{irker}->{recipients} ne
            join(' ', map { "#$_" } @{ $self->config->irc_channel })) {
            push @err, "bad irc channel: " . $hooks->{irker}->{recipients};
        } elsif ($self->config->disable_irker and $hooks->{irker}) {
            push @err, "irker enabled";
        }
        # Tagpending
        if ($self->config->tagpending and not $hooks->{tagpending}) {
            push @err, "tagpending missing";
        } elsif ($self->config->disable_tagpending
            and $hooks->{tagpending}) {
            push @err, "tagpending enabled";
        }
        # report errors
        if (@err) {
            $res++;
            push @fail, $name;
            print "$name:\n";
            print "\t$_\n" foreach (@err);
        } else {
            ds_verbose "$name: OK";
        }
    }
    return ($res, \@fail);
}

1;
