package utils;
use strict;
use warnings;
use utf8;
use Carp qw(croak);

use LWP::UserAgent;
use JSON;
my $ua = LWP::UserAgent->new;
my $json = JSON->new->utf8->indent;

sub json_api {
    my $url = shift;
    
    my $res = $ua->get(
        "https://api.github.com$url"
    );
    
    $res->is_success or croak $res->status_line;
    
    return $json->decode($res->content);
}


package GitHubBackup;

use strict;
use warnings;
use utf8;
use Carp qw(croak);
use File::Spec;


# both hash and hashref are acceptable
sub new {
    my $class = shift;
    my $args = (ref $_[0] eq 'HASH') ? $_[0] : {@_};
    
    return bless $args, $class;
}

sub account {
    my $self = shift;
    my $args = shift;
    
    if (defined $args) {
        $self->{repos} = undef;
        $self->{account} = $args;
    }
    
    return $self->{account};
}

sub repository {
    my $self = shift;
    my $args = shift;
    
    if (defined $args) {
        $self->{repos} = undef;
        $self->{repository} = $args;
    }
    
    return $self->{repository};
}

sub directory {
    my $self = shift;
    my $args = shift;
    
    if (defined $args) {
        $self->{directory} = File::Spec->rel2abs($args);
    }
    
    return $self->{directory};
}

sub repos {
    my $self = shift;
    return $self->{repos} if ($self->{repos});
    
    my $account = $self->account or croak "account is not set";
    if (my $repository = $self->repository) {
        $self->{repos} = [
            GitHubBackup::Repository->new({
                directory => sub {$self->directory},
                full_name => "$account/$repository",
            })
        ];
        
        return $self->{repos};
    }
    
    my $page = 1;
    my @repos;
    while (1) {
        my $result = utils::json_api("/users/$account/repos?per_page=100&page=$page");
        if (ref($result) eq 'ARRAY' && scalar @$result > 0) {
            push @repos, @$result;
            $page++;
            
            next;
        }
        last;
    }
    
    foreach my $repos (@repos) {
        push @{$self->{repos}},
            GitHubBackup::Repository->new({
                directory => sub {$self->directory},
                full_name => $repos->{full_name},
                clone_url => $repos->{clone_url},
            })
        ;
    }
    
    return $self->{repos};
}

sub backup {
    my $self = shift;
    
    foreach my $repos (@{$self->repos}) {
        $repos->backup;
    }
    
    return $self;
}


package GitHubBackup::Repository;

use strict;
use warnings;
use utf8;
use Carp qw(croak);
use Git::Repository;
use File::chdir;
use File::Spec;
use File::Path qw(mkpath);

sub new {
    my $class = shift;
    my $args  = shift;
    
    if (! exists $args->{clone_url}) {
        my $result = utils::json_api('/repos/' . $args->{full_name});
        $args->{clone_url} = $result->{clone_url};
    }
    
    return bless $args, $class;
}

sub directory {
    my $self = shift;
    
    my $path = $self->{full_name};
    if (my $base = $self->{directory}->()) {
        $path = File::Spec->catfile($base, $path);
    }
    
    return $path;
}

sub clone_git {
    my $self = shift;
    
    my $dir = $self->directory;
    if (-d "$dir") {
        local $CWD = $dir;
        print "fetch ", $dir, "\n";
        Git::Repository->run(fetch => '--all');
        return $self;
    }
    
    print "clone ", $dir, "\n";
    mkpath $dir;
    Git::Repository->run(clone => '--mirror' => $self->{clone_url} => $dir);
    return $self;
}

sub set_forks {
    my $self = shift;
}

sub clone_wiki {
    my $self = shift;
}

sub save_issues {
    my $self = shift;
}

sub backup {
    my $self = shift;
    
    $self->clone_git;
    $self->set_forks;
    $self->clone_wiki;
    $self->save_issues;
    
    return $self;
}


1;
__END__
