#!/usr/bin/env perl

# Copyright 2015-2021 SUSE LLC
# SPDX-License-Identifier: GPL-2.0-or-later

=head1 NAME

load_templates - load openqa job templates

=head1 SYNOPSIS

load_templates [OPTIONS] FILE

=head1 OPTIONS

=over 4

=item B<--host> HOST

connect to specified host, defaults to localhost

=item B<--apibase>

Set API base URL component, default: '/api/v1'

=item B<--apikey> KEY, B<--apisecret> SECRET

Specify api key and secret to use, overrides use of config file ~/.config/openqa/client.conf

override values from config file

=item B<--clean>

delete all job templates before loading new ones. be careful!

=item B<--update>

update existing entries (by default, existing entries are not changed)

=item B<--help, -h>

print help

=back

=head1 DESCRIPTION

lorem ipsum ...

=cut

use Mojo::Base -strict, -signatures;

use FindBin;
use lib "$FindBin::RealBin/../lib";
use File::Basename qw(dirname);
use Try::Tiny;
use Data::Dump 'dd';
use Mojo::Util qw(decamelize);
use Mojo::URL;
use OpenQA::Client;
#use OpenQA::Script::Client;
use Mojo::JSON;    # booleans
use Cpanel::JSON::XS ();

use Getopt::Long;

Getopt::Long::Configure("no_ignore_case");

my %options;

sub usage ($r) {
    eval "use Pod::Usage; pod2usage($r);";
    die "cannot display help, install perl(Pod::Usage)\n" if $@;
}

GetOptions(\%options, "apibase=s", "apikey=s", "apisecret=s", "clean", "host=s", "update", "help|h",) or usage(1);

usage(0) if $options{help};
usage(1) if $#ARGV;

# Slurp file specified as an argument or STDIN in case of "-"
my $datafile = join("", <>);
die "Data file not found\n" unless length $datafile;

my $info;

try {
    $info = Cpanel::JSON::XS->new->relaxed->decode($datafile);
    dd $info;
}
catch {
    $info = eval $datafile;
    if (my $error = $@) { die "Error in data file: $error\n" }
};

$options{'host'} ||= 'localhost';
$options{apibase} ||= '/api/v1';
my $url = OpenQA::Client::url_from_host($options{host});
my $client = OpenQA::Client->new(apikey => $options{'apikey'}, apisecret => $options{'apisecret'}, api => $url->host);
my @tables = (qw(Machines TestSuites Products JobTemplates JobGroups));

sub print_error ($res) {
    if (my $err = $res->error) {
        if (my $json = $res->json) {
            if (my $json_err = $json->{error}) {
                die "$err->{message}: " . join("\n", @{$json_err})
                  if ref $json_err eq 'ARRAY';
                die "$err->{message}: $json_err";
            }
        }
        die "ERROR: $err->{code} - $err->{message}" if $err->{code};
    }
    die "unknown error code - host $url->{host} unreachable?";
}

sub post_entry ($table, $entry) {
    my %param;

    if ($table eq 'JobGroups') {
        # Create the group first
        my $job_groups_url = $url->clone->path($options{apibase} . '/job_groups');
        my $res = $client->post($job_groups_url, form => {name => $entry->{group_name}})->res;
        print_error $res unless $res->is_success;

        # Post the job template YAML
        my $job_templates_url = $url->clone->path($options{apibase} . '/job_templates_scheduling');
        $res
          = $client->post($job_templates_url,
            form => {name => $entry->{group_name}, template => $entry->{template}, schema => 'JobTemplates-01.yaml'})
          ->res;
        print_error $res unless $res->is_success;
        return 1;
    }

    if ($table eq 'JobTemplates') {
        unless (defined($entry->{prio})) {
            # we have to migrate the prio from the TestSuite to the JobTemplate
            for my $ts (@{$info->{TestSuites}}) {
                if ($ts->{name} eq $entry->{test_suite}{name}) {
                    $entry->{prio} = $ts->{prio};
                }
            }
        }
        unless (defined($entry->{group_name})) {
            # we have to create a group_name from the Product
            my $gn = $entry->{product}{distri};
            if ($entry->{product}{version} ne '*') {
                $gn .= "-" . $entry->{product}{version};
            }
            $entry->{group_name} = $gn;
        }
    }

    for my $key (keys %{$entry}) {
        if ($key eq 'machine' && defined $entry->{machine}{name}) {
            $param{machine_name} = $entry->{machine}{name};
        }
        elsif ($key eq 'test_suite' && $entry->{test_suite}{name}) {
            $param{test_suite_name} = $entry->{test_suite}{name};
        }
        elsif ($key eq 'product' && ref($entry->{product}) eq "HASH") {
            $param{arch} = $entry->{product}{arch};
            $param{distri} = $entry->{product}{distri};
            $param{flavor} = $entry->{product}{flavor};
            $param{version} = $entry->{product}{version};
        }
        elsif ($key eq 'settings' && ref($entry->{settings}) eq "ARRAY") {
            for my $var (@{$entry->{settings}}) {
                $param{'settings[' . $var->{key} . ']'} = $var->{value};
            }
        }
        else {
            $param{$key} = $entry->{$key};
        }
    }

    my $table_url = $url->clone->path($options{apibase} . '/' . decamelize($table));
    if (!$options{clean}) {    # with --clean the entry should not exist at this point, no need to check
        my $res = $client->get($table_url->clone, form => \%param)->res;
        if ($res->is_success && @{$res->json->{$table}} > 0 && $res->json->{$table}[0]{id}) {
            if ($options{update} && $table ne 'JobTemplates')
            {    # there is nothing to update in JobTemplates, the entry just exists or not
                my $id = $res->json->{$table}[0]{id};
                my $table_url_id = $url->clone->path($options{apibase} . '/' . decamelize($table) . "/$id");
                my $res = $client->put($table_url_id, form => \%param)->res;
                print_error $res unless $res->is_success;
                return 1;
            }
            else {
                return 0;    # already exists
            }
        }
    }

    my $res = $client->post($table_url, form => \%param)->res;
    print_error $res unless $res->is_success;
    return 1;
}

if ($options{'clean'}) {
    for my $table (@tables) {
        # we can't clean job groups as they're not deletable unless empty
        next if ($table eq "JobGroups");
        my $table_url = $url->clone->path($options{apibase} . '/' . decamelize($table));
        my $res = $client->get($table_url)->res;
        if ($res->is_success) {
            my $result = $res->json;
            for my $i (0 .. $#{$result->{$table}}) {
                my $id = $result->{$table}->[$i]->{id};
                my $table_url_id = $url->clone->path($options{apibase} . '/' . decamelize($table) . "/$id");
                $res = $client->delete($table_url_id)->res;
                last unless $res->is_success;
            }
        }

        print_error $res unless $res->is_success;
    }
}

my %added;
for my $table (@tables) {
    next unless $info->{$table};
    $added{$table}->{of} = @{$info->{$table}};
    $added{$table}->{added} = 0;
    for my $entry (@{$info->{$table}}) {
        if (post_entry($table, $entry)) {
            ++$added{$table}->{added};
        }
    }
}

dd \%added;
