#!/usr/bin/perl

use strict;
use warnings;
use YAML;
use Data::Dumper;
use JSON::MaybeXS;
use Net::Docker::Registry::Client;
use Template;
use Encode;
use URI::Escape;
use List::Util qw(first reduce);

my $cfg_file = "/etc/cooverview/config.yml";

if ($ENV{NET_SERVER_TYPE} && $ENV{NET_SERVER_TYPE} eq 'Net::Server::PreFork') {
  $cfg_file = "./config.yml";
}

my $config = YAML::LoadFile($cfg_file);
my $drc = Net::Docker::Registry::Client->new(%$config);
my $tconfig = {
  INCLUDE_PATH => $config->{templates_dir} || '/etc/cooverview/templates'
};

my $template = Template->new($tconfig);

print "Content-type:text/html\r\n\r\n";

my $predefined_search_buttons = [
  {name => "Official (default)", search_term => "project=^openSUSE:Containers: container=.*", css_class => 'success' },
  {name => "Devel (default)"   , search_term => "project=^(?!(open)?SUSE|kubic|home):')"    , css_class => 'warning' },
  {name => "Home (default)"    , search_term => "project=^home"                             , css_class => 'danger'  },
  {name => "All (default)"     , search_term => ""                                          , css_class => 'primary' },
];

my $stm_internal = $predefined_search_buttons->[0]->{search_term};

my $template_default_data = {
    default_search_term => $config->{default_search_term}
      || $config->{search_buttons}->[0]->{search_term}
      || $stm_internal,
};

if ($ENV{REQUEST_METHOD} eq 'POST') {
  my $content;
  read(*STDIN, $content, $ENV{CONTENT_LENGTH}) || die "Cannot read from stdin $ENV{CONTENT_LENGTH}";
  my $data;
  eval {
    $data = decode_json($content);
  };
  if ($@) {
    $data = {};
    for my $parts (split('\?', $content)) {
      for my $part (split(/&/, $parts)) {
        my ($key, $val) = split('=', $part, 2);
        $key =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
        $val =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
        $val =~ s/\+/ /g;
        $data->{$key} = $val;
      }
    }

    create_start_page($config, $drc, $data, $template_default_data);
  } else {
    create_details_html($drc, $data->{container}, $data->{pull_command});
  }
} else {
  my %search = split(/[&=]/, $ENV{QUERY_STRING} || "");
  create_start_page(
    $config,
    $drc,
    {
      srch_term => Encode::decode('utf8', uri_unescape($search{q}))
        || $template_default_data->{default_search_term}
    },
    $template_default_data,
  );
}

exit 0;

################################################################################
# SUBS
################################################################################
sub create_start_page {
  my ($config, $drci, $data, $template_default_data) = @_;
  my $content;
  my $repos;
  my $h_repos;
  my $error;

  eval {
    if ($config->{filter_source_projects}) {
      $h_repos = $drc->obs_info_json("")->{owners};
    } else {
      my $tmp_repos = $drc->repositories();
      $h_repos->{$_} = $_ for @{$tmp_repos};
    }
  };

  if ($@) {
    $error = "An error occured while generating repository list: $@";
  } else {
    my $tmp_repos = [];
    my $srch = {};
    foreach my $tmp (split(/ /, $data->{'srch_term'})) {
      my ($field, $regex) = split(/=/, $tmp, 2);
      $srch->{$field} = $regex;
    }
    while (my ($repo, $val) = each %{$h_repos}) {
      if ($config->{filter_source_projects}) {
        next if (defined($srch->{project}) && $val !~ $srch->{project});
        next if (defined($srch->{container}) && $repo !~ $srch->{container});
        push(@{$tmp_repos}, $repo);
      } else {
        push(@{$tmp_repos}, $repo) if ($val =~ /$data->{'srch_term'}/);
      }
    }
    @{$repos} = sort @{$tmp_repos};
  }


  my $sb = $config->{search_buttons} || $predefined_search_buttons;

  $template->process(
    'index.html.tt2',
    {
      %{$template_default_data},
      error                     => $error,
      repositories              => $repos,
      css_class                 => $config->{css_class} || 'btn-link text-decoration-none',
      theme                     => $config->{theme},
      use_obs_extended_info     => $config->{use_obs_extended_info},
      build_host                => $config->{build_host},
      search_buttons            => $sb,
      srch_term                 => $data->{srch_term} || '',
    }
  ) || die $template->error();
}

sub create_details_html {
  my ($drc, $rep, $pcommand) = @_;
  my $content;
  my $error;
  my $tags = [];
  my $info;

  my $pull_prefix="$pcommand pull $config->{pull_host}".(($config->{pull_port}) ? ":$config->{pull_port}/" : "/");

  my $data = {
    name => $rep,
    tags => [],
    use_obs_extended_info => $config->{use_obs_extended_info},
  };

  eval {
    if ($config->{use_obs_extended_info}) {
      $data->{info} = $drc->obs_info_json($rep);
      $data->{project} = $data->{info}->{project};
      if ($config->{build_host}) {
        $data->{project_href} = ($config->{build_host}->{proto} || 'https')."://$config->{build_host}->{host}/project/show/$data->{info}->{project}";
      }
      $data->{repository} = $data->{info}->{repository};
      while (my ($tag, $tag_val) = each %{$data->{info}->{tags}}) {
	foreach my $img (@{$tag_val->{images}}) {
	  $img->{id} = $img->{imageid};
	  $img->{id} =~ s/.*://;
	  $img->{id} = substr $img->{id}, 0, 12;
	  $img->{size} = 0;
	  $img->{size} += $_->{blobsize} for @{$img->{layers}};
	  $img->{size} = int($img->{size} / (1024 * 1024)) . "MB";
	}
	my $newest = reduce { $a->{buildtime} > $b->{buildtime} ? $a : $b } @{$tag_val->{images}};
	$tag_val->{buildtime} = $newest->{buildtime};
	my $tag_data = {
	  pull_command => "$pull_prefix$rep:$tag",
	  name         => $rep,
	  tag          => $tag,
	};
	push @{$data->{tags}}, $tag_data;
      }
      $_->{buildtime} = @{$data->{info}->{tags}}{$_->{tag}}->{buildtime} for @{$data->{tags}};
      @{$data->{tags}} = sort { $b->{buildtime} <=> $a->{buildtime} || $a->{tag} cmp $b->{tag} } @{$data->{tags}};
    } else {
      my $all_tags = $drc->list_tags($rep);
      die "No tags defined for this image\n" unless $all_tags;
      for my $tag (@{$all_tags}) {
	my $manifest = $drc->manifests($rep, $tag);
	my $tag_data = {
	  pull_command => "$pull_prefix$rep:$tag",
	  manifest     => $manifest,
	  name         => $rep,
	  tag          => $tag,
	};
	push @{$data->{tags}}, $tag_data;
      }
    }
    if ($#{$data->{tags}} > 0) {
      my $latest_index = first { @{$data->{tags}}[$_]->{tag} eq 'latest' } 0..$#{$data->{tags}};
      my $latest = splice(@{$data->{tags}}, $latest_index, 1);
      unshift(@{$data->{tags}}, $latest);
    }
  };

  $data->{error} = $@ if $@;
  $template->process('details.tt2', $data) || die $template->error();
}

sub logit {
  my ($msg) = @_;
  return unless $config->{logit};
  my $logfile= '/var/log/apache2/cooverview.log';
  open(my $fh, '>>', $logfile) || die "Could not open '$logfile': $!";
  $msg = (ref $msg) ? Dumper($msg) : $msg;
  print $fh $msg;
  close $fh;
}
