#!/usr/bin/env perl
# Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
# Copyright [2016-2022] EMBL-European Bioinformatics Institute
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


use strict;
use warnings;

BEGIN {
  use Cwd;
  use File::Basename;
  use File::Spec;
  my $dirname = dirname(Cwd::realpath(__FILE__));
  my $lib = File::Spec->catdir($dirname, File::Spec->updir(), 'lib');
  if(-d $lib) {
    unshift(@INC, $lib);
  }
  else {
    die "Cannot find the lib directory in the expected location $lib";
  }
};

use EnsEMBL::Git qw/bare_clone fetch/;
use EnsEMBL::GitHub qw/parse_oauth_token rest_request paginated_rest_request/;
use Pod::Usage;
use Getopt::Long;

run();

sub run {
  my $opts = parse_command_line();
  sync_repos($opts);
  return;
}

sub sync_repos {
  my ($opts) = @_;
  my $repos = get_repos($opts);
  foreach my $repo (@{$repos}) {
    my $name = $repo->{name};
    my $url = $repo->{clone_url};
    print "* Working with $name\n" if $opts->{verbose};
    my $dir = File::Spec->catdir($opts->{dir}, $name.'.git');
    my $action;
    my $cwd = cwd();
    if(! -d $dir) {
      print "* $dir does not exist; cloning repo rather than fetching\n" if $opts->{verbose};
      if( ! $opts->{'dry-run'}) {
        chdir $opts->{dir};
        bare_clone($url, $opts->{verbose}) or die 'Cannot clone repo '.$name; 
      }
    }
    else {
      print "* Fetching new information from repo (code and tags)\n" if $opts->{verbose};
      if(! $opts->{'dry-run'}) {
        chdir $dir;
        fetch(undef, $opts->{verbose}) or die 'Cannot fetch new commits for '.$name;
        fetch('tags', $opts->{verbose}) or die 'Cannot fetch tags for '.$name;
      }
    }
    chdir $cwd;
  }
}

sub get_repos {
  my ($opts) = @_;
  my $organisation = $opts->{organisation};
  my $oauth_token = get_oauth_token($opts);
  my $repos = paginated_rest_request('GET', "/orgs/${organisation}/repos", $oauth_token);
  return $repos unless @{$opts->{repository}};
  my %lookup = map { $_, 1 } @{$opts->{repository}};
  return [ grep { $lookup{$_->{name}} } @{$repos} ];
}

sub get_oauth_token {
  my ($opts) = @_;
  return $opts->{oauth} if $opts->{oauth};
  return if ! $opts->{oauth_file};
  my $path = $opts->{oauth_file};
  my $token = parse_oauth_token($path);
  return $opts->{oauth} = $token;
}

sub parse_command_line {
  my $opts = {
    repository => [],
    organisation => 'Ensembl',
    help => 0,
    man => 0
  };

  GetOptions($opts, qw/
    repository|repo=s@
    dir|directory=s
    organisation|organization=s
    oauth=s
    oauth_file=s
    dry-run
    verbose
    help|?
    man
  /) or pod2usage(2);

  pod2usage(1) if $opts->{help};
  pod2usage(-exitval => 0, -verbose => 2) if $opts->{man};

  if(!$opts->{dir}) {
    pod2usage(-exitval => 1, -verbose => 1, -msg => 'You must specify a --directory to work with');
  }

  return $opts;
}
__END__
=pod

=head1 NAME

github-sync - Sync an organisation's repositories

=head1 SYNOPSIS

github-sync --directory DIR [--organisation ORG] [--repository REPO] [--oauth OAUTH || --oauth_file FILE] [--dry-run] [-h] [-m]

# Sync all repos in current dir

github-sync --directory $PWD

# Sync a selection

github-sync --directory $PWD --repository ensembl-hive

# Dry run with chatter

github-sync --directory $PWD --dry-run --verbose

=head1 DESCRIPTION

A script to pull down all available repositories from a GitHub organisation and attempts to pull the latest set of commits down from the remote GitHub repositories.

=head1 OPTIONS

=over 8

=item B<--directory>

Specify the directory to synchronize against

=item B<--organisation|organization>

The GitHub organisation to list repositories for. Defaults to Ensembl

=item B<--repository|repo>

The repository to use. If not specified we use all public repositories

=item B<--oauth>

The OAuth token to use. More information is available from L<http://developer.github.com/v3/#authentication> and can be generated from your personal settings page. Not required.

=item B<--oauth_file>

The file which contains the OAuth key. Must be just the OAuth token (any whitespace will be removed). The file must be read/write only by the user and its containing directory must be read/write/execute by the user alone. Not required.

=item B<--dry-run>

Do not fetch or clone

=item B<--help>

Print the help information

=item B<--man>

Print a man page

=back

=cut
