#!/usr/bin/perl

# Copyright (C) 2015 SUSE Linux GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, see <http://www.gnu.org/licenses/>.

=head1 load_templates

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 FindBin;
use lib "$FindBin::RealBin/../lib";
use strict;
use File::Basename qw(dirname);
use Try::Tiny;
use Data::Dump 'dd';
use Mojo::Util qw(decamelize);
use Mojo::URL;
use OpenQA::Client;
use Mojo::JSON;    # booleans
use Cpanel::JSON::XS ();

use Getopt::Long;

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

my %options;

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

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

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

my $datafile = shift @ARGV;
die "Data file not found\n" unless $datafile && -r $datafile;

my $info;

if ($datafile =~ /\.json$/) {
    local $/;
    open(my $fh, '<', $datafile);
    $info = Cpanel::JSON::XS->new->relaxed->decode(<$fh>);
    close $fh;
    dd $info;
}
else {
    $info = do $datafile;
    if (my $error = $@) {
        die "Error in data file: $error\n";
    }
}

$options{'host'}    ||= 'localhost';
$options{'apibase'} ||= '/api/v1';

my $url;
if ($options{'host'} !~ '/') {
    $url = Mojo::URL->new();
    $url->host($options{'host'});
    $url->scheme('http');
}
else {
    $url = Mojo::URL->new($options{'host'});
}

my $client = OpenQA::Client->new(apikey => $options{'apikey'}, apisecret => $options{'apisecret'}, api => $url->host);

my @tables = (qw(Machines TestSuites Products JobTemplates));

sub print_error {
    my ($res) = @_;
    printf STDERR "ERROR: %s - %s\n", $res->code // 'unknown error code', $res->message // 'no error message';
    if ($res->body) {
        dd($res->json || $res->body);
    }
}

sub post_entry {
    my ($table, $entry) = @_;

    my %param;

    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};
        }
    }

    if (!$options{'clean'}) {    # with --clean the entry should not exist at this point, no need to check
        my $res = $client->get($url->path($options{'apibase'} . '/' . decamelize($table))->query(%param))->res;
        if ($res->code == 200 && @{$res->json->{$table}} == 1 && $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 $res
                  = $client->put($url->path($options{'apibase'} . '/' . decamelize($table) . "/$id")->query(%param))
                  ->res;
                if ($res->code != 200) {
                    print_error($res);
                    return 0;
                }
                return 1;
            }
            else {
                return 0;        # already exists
            }
        }
    }

    my $res = $client->post($url->path($options{'apibase'} . '/' . decamelize($table))->query(%param))->res;

    if ($res->code != 200) {
        print_error($res);
        return 0;
    }
    return 1;
}

if ($options{'clean'}) {
    for my $table (@tables) {
        my $res = $client->get($url->path($options{'apibase'} . '/' . decamelize($table)))->res;
        if ($res->code == 200) {
            my $result = $res->json;
            for my $i (0 .. $#{$result->{$table}}) {
                my $id = $result->{$table}->[$i]->{id};
                $res = $client->delete($url->path($options{'apibase'} . '/' . decamelize($table) . "/$id"))->res;
                last if $res->code != 200;
            }
        }

        if ($res->code != 200) {
            print_error($res);
            exit(1);
        }
    }
}

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;
