#!/usr/bin/perl
#
# Technical Report tool for managing DOI registrations
#
# Markus Kuhn <https://www.cl.cam.ac.uk/~mgk25/>

use 5.016;
use warnings;
no warnings 'uninitialized';
use English;
use open ':utf8';
use utf8;                     # this file is UTF-8 encoded
use JSON::PP;
use HTTP::Tiny;
use FindBin qw($RealBin);     # find directory where this file is located ...
use lib $RealBin;             # ... and add it to @INC
use TechReports;              # this also includes package/class TechReport

use lib '/anfs/www/tools/share';
use PasswordVault;

binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";

my $usage = <<End;
TR-DOI -- Manage DataCite DOIs for technical reports -- Markus.Kuhn\@cl.cam.ac.uk

Usage: tr-doi {commands}

Commands:

  create<nr>     create a draft DOI record for TR-<nr> (without registering)

  update<nr>     create or update a record for TR-<nr> (without changing state)

  publish<nr>    create or update a record for TR-nr and change its state
                 to findable

  register<nr>   create or update a record for TR-nr and change its state
                 from draft to registered

  hide<nr>       create or update a record for TR-nr and change its state
                 from findable to registered

See https://support.datacite.org/docs/api-create-dois for an explanation
of DataCite's DOI states.

We always request a fixed DOI, i.e. we don't use DOI auto-generation,
therefore update etc. are all idempotent and there is no real need
to use the "create" operation. (It is just implemented for completeness.)

Normally, simply use publish<nr> to get a new DOI in a single step.
End

die $usage unless @ARGV;

# Read-in database
my $dbfile = 'tr-database.txt';
my $absfile = 'tr-abstracts.txt';
map { $_ = "$RealBin/$_" } $dbfile, $absfile
    unless -e $dbfile;
my $db = TechReports->load($dbfile, $absfile);

my ($datacite_login, $datacite_apihost, $doi_prefix);
my $test = 0;
if ($test) {
    # CL test account
    # https://doi.test.datacite.org/repositories/mqou.riwtfs
    $doi_prefix = '10.81026';
    $datacite_login = 'MQOU.RIWTFS';
    $datacite_apihost = 'api.test.datacite.org';
} else {
    # CL production account
    # https://doi.datacite.org/repositories/lpsw.zmftcd
    $doi_prefix = '10.48456';
    $datacite_login = 'LPSW.ZMFTCD';
    $datacite_apihost = 'api.datacite.org';
}
if (exists $db->{doiprefix} && $db->{doiprefix} ne $doi_prefix) {
    die("DOI prefix mismatch: $db->{doiprefix} != $doi_prefix\n")
}

my $datacite_passwd = PasswordVault::fetch_password($datacite_login,
                                                    'datacite.org');
my $datacite_url =
    "https://$datacite_login:$datacite_passwd\@$datacite_apihost";
my $http = HTTP::Tiny->new(verify_SSL => 1);

# process command line arguments
while ($_ = shift(@ARGV)) {
    if (/^(create|update|publish|register|hide)([^:]+)$/) {
        my $op = $1;
        for my $tr ($db->range($2)) {
            # https://support.datacite.org/docs/api
            # https://doi.test.datacite.org/dois
            # check for some required attributes
            unless ($tr->year) {
                warn("skipping $tr->{nr} due to missing publication year\n");
                next;
            }
            unless ($tr->{title}) {
                warn("skipping $tr->{nr} due to missing title\n"); next
            }
            unless (@{$tr->{authors}}) {
                warn("skipping $tr->{nr} due to missing authors\n"); next
            }
            if ($tr->{'to-appear'}) {
                warn("skipping $tr->{nr} as it is yet to appear\n"); next
            }
            my $attributes = $tr->datacite_json;
            if ($op eq 'publish' ||   # draft | registered -> findable
                $op eq 'register' ||  # draft -> registered
                $op eq 'hide') {      # findable -> registered
                # change DOI state
                # see https://support.datacite.org/docs/api-create-dois
                $attributes->{event} = $op
            }
            my $request = {
                data => {
                    attributes => $attributes
                }
            };
            my $method;
            my $url;
            if ($op eq 'create') {
                $request->{data}->{type} = 'dois';
                $method = 'POST';
                $url = "$datacite_url/dois";
            } elsif ($op eq 'update' || $op eq 'register' || $op eq 'publish') {
                $method = 'PUT';
                $url = "$datacite_url/dois/" . $tr->doi;
            }
            my $content = encode_json $request;
            #say "$method $url";
            #say "$content";
            my $response = $http->request(
                $method, $url,
                {
                    headers => { 'Content-Type' => 'application/vnd.api+json' },
                    content => $content
                });
            say "$op DOI of TR-$tr->{nr}: $response->{status} $response->{reason}";
            #print $response->{content} if length $response->{content};
        }
    } else {
        die("Unknown command '$_'\n");
    }
}
