#!/usr/bin/perl -w
#
# Copyright (c) 2022 Adrian Schroeter, SUSE LLC
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# 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 (see the file COPYING); if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
################################################################
#
# OBS Git LFS provider
# 
# This service answers git LFS requests and let OBS deliver the files
# based on a static database.
#
# This is only supposed to be used for converted sources from classic
# OBS source repositories. This is not supposed to be used for sources
# which are supposed to be actively maintained in git directly.
#

BEGIN {
  my ($wd) = $0 =~ m-(.*)/- ;
  $wd ||= '.';
  unshift @INC,  "$wd";
}

use Data::Dumper;
use JSON::XS ();

use BSServer;
use BSRPC ':https';
use BSDispatch;
use BSXML;

use strict;

my $port = 9998;
my $proto = 'http';

my $obsurl = "https://api.opensuse.org/public";
my $header = "X-SCM-BRIDGE-COOKIE: not_configured";
my $queue_dir = "/srv/obs-gitea-bridge/queue";
eval {
  use config;
  $header = "X-SCM-BRIDGE-COOKIE: $config::shared_cookie" if $config::shared_cookie;
  $obsurl = $config::obsurl;
  $queue_dir = $config::queue_dir if $config::queue_dir;
};

sub repositoryresults {
  my ($cgi) = @_;

  my $scmrepo = $cgi->{'scmrepository'};
  my $scmbranch = $cgi->{'scmbranch'};

  my $query = {'scmrepository' => $scmrepo,
	       'scmbranch'     => $scmbranch };
  my @args = BSRPC::args($query, 'scmrepository', 'scmbranch');
  my $r = BSRPC::rpc({'uri' => "$obsurl/build/_result",
	              'request' => 'GET',
	              'headers' => [$header]},
                      $BSXML::resultlist,
                     @args);

  my $html = '<table> <tr> <th>Project</th><th>Package</th> <th>Repository</th> <th>Architecture</th> <th>Result</th> </tr>';
  my $lastproject = '';
  my $lastpackage = '';
  my $lastrepo = '';
  my $count = 0;
  for my $result (@{$r->{'result'} || []}) {
    my $project = $result->{'project'};
    my $package = $result->{'status'}[0]->{'package'} || undef;
    my $repo    = $result->{'repository'};
    my $arch    = $result->{'arch'};
    my $code    = $result->{'status'}[0]->{'code'} || 'unknown';

    $project = '' if $project eq $lastproject;
    $package = '' if $package eq $lastpackage;
    $repo = ''    if $repo eq $lastrepo;
    $count = $count + 1;

    my $bgcolor = "#fff";
    $bgcolor = "#bbb" if $code eq 'scheduled' || $code eq 'building' ||  $code eq 'finished' || $code eq 'blocked';
    my $color = "#FFF";
    $color = "#6EB927" if $code eq 'succeeded';
    $color = "#666" if $code eq 'signing';
    $color = "#222" if $code eq 'finished';
    $color = "#c00" if $code eq 'unresolvable';
    $color = "#e00" if $code eq 'broken';
    $color = "#f00" if $code eq 'failed';
    $color = "#fff" if $code eq 'blocked';

    $html .= "<tr><td>$project</td><td>$package</td> <td>$repo</td> <td>$arch</td> <td bgcolor=\"$bgcolor\"><font color=\"$color\">$code</font></td> </tr>\n";

    $lastproject = $project unless $project eq '' ;
    $lastpackage = $package unless $package eq '' ;
    $lastrepo    = $repo    unless $repo eq '';
    last if $count > 10;
  };
  $html .= '</table>';
  $html .= '<p>.... uncollapsing not implemeted...</p>' if $count > 10;
  return $html;
}

sub process_queue {
  return unless -d $queue_dir;

  opendir(DIR_HANDLE, $queue_dir) or return;
  my @files = sort grep { /^\d+\.json$/ } readdir(DIR_HANDLE);
  closedir(DIR_HANDLE);
  return unless @files;

  for my $file (@files) {
    my $filepath = "$queue_dir/$file";
    my $content = do { local $/; open(my $fh, '<', $filepath) or next; binmode($fh); <$fh> };
    my $batch = JSON::XS::decode_json($content);
    next unless $batch && $batch->{'ref'} && $batch->{'repository'}->{'clone_url'};

    my $branch = $batch->{'ref'};
    $branch =~ s/^refs\/heads\///;
    $branch =~ s/^refs\/tags\///;
    $branch =~ s/^refs\///;

    my $cgi = { 'cmd'           => 'triggerscmsync',
		'scmrepository' => $batch->{'repository'}->{'clone_url'},
		'scmbranch'     => $branch };
    $cgi->{'isdefaultbranch'} = 1 if $batch->{'repository'}->{'default_branch'} eq $branch;
    my @args = BSRPC::args($cgi, 'cmd', 'scmrepository', 'scmbranch', 'isdefaultbranch');

    eval {
      my $h = BSRPC::rpc({'uri' => "$obsurl/source",
      		  	   'request' => 'POST',
      		  	   'headers' => [$header],
      		  	   'timeout' => 30}, undef, @args);
    };
    if ($@) {
      print "Queue: failed to process $file: $@\n";
      last;
    } else {
      unlink($filepath) or print "Queue: failed to delete $filepath: $!\n";
      print "Processed: $batch->{'repository'}->{'clone_url'}#$branch from $file\n";
    }
  }
}

sub repositoryevent {
  die("need event data as content\n") unless BSServer::have_content();
  my $batch_json = BSServer::read_data(10000000);
  my $batch = JSON::XS::decode_json($batch_json);

  die("no ref given\n") unless $batch->{'ref'};
  die("no repository given\n") unless $batch->{'repository'};
  die("no clone repository given\n") unless $batch->{'repository'}->{'clone_url'};

  # write event to queue file
  my $timestamp = time();
  my $filepath = "$queue_dir/$timestamp.json";
  open(my $fh, '>', $filepath) or die("cannot write queue file $filepath: $!\n");
  binmode($fh);
  print $fh $batch_json;
  close($fh);

  # try to process queue
  process_queue();

  my $resp = {
    answer => 'thank you',
  };
  #  print Dumper(JSON::XS::encode_json($resp));
  # print "Received: $filepath\n";
  return (JSON::XS::encode_json($resp), 'Content-Type: application/json');
}

sub hello {
  return ("{\"message\": \"OBS Gitea Bridge\"}\n", 'Content-Type: application/json');
}

sub errorreply {
  my ($err, $code, $tag, @hdrs) = @_;
  $err = JSON::XS::encode_json({'message' => $tag});
  BSServer::reply("$err\n", "Status: $code $tag", 'Content-Type: application/json', @hdrs);
}

sub dispatch {
  my ($conf, $req) = @_;
  return BSDispatch::dispatch($conf, $req);
}

# define server
my $dispatches = [
  '/' => \&hello,
  'GET:/repositoryresults scmrepository:? scmbranch:? code:? lastbuild:? multibuild:? locallink:?' => \&repositoryresults,
  'POST:/repositoryevent' => \&repositoryevent,
];

my $conf = {
  'port' => $port,
  'proto' => $proto,
  'dispatch' => \&dispatch,
  'dispatches' => $dispatches,
  'errorreply' => \&errorreply,
  'setkeepalive' => 1,
  'maxchild' => 20,
};

# ensure queue directory exists
mkdir($queue_dir) or die("cannot create queue directory $queue_dir: $!\n") unless -d $queue_dir;

# process any leftover events from previous run
process_queue();

BSDispatch::compile($conf);
BSServer::serveropen($conf->{'port'});
BSServer::server($conf);
