#!/usr/bin/env perl 
use strict;
use warnings;

=pod

Helper script to extract field and type information from documentation.

Run without parameters, it will cache data to C<docs.html> locally and will
update the files in C<lib/WebService/Async/Onfido/*>.

=cut

use IO::Async::Loop;
use Net::Async::HTTP;
use Path::Tiny;
use HTML::TreeBuilder;
use Template;
use List::UtilsBy qw(extract_by);

use Log::Any qw($log);
use Log::Any::Adapter qw(Stderr), log_level => 'trace';

my $loop = IO::Async::Loop->new;
$loop->add(
    my $ua = Net::Async::HTTP->new
);

my $data = do {
    my $path = path('docs.html');
    $path->exists ? $path->slurp_utf8 : do {
        my $resp = $ua->GET('https://documentation.onfido.com')->get;
        $path->spew_utf8(my $txt = $resp->decoded_content);
        $txt
    }
};
my $html = HTML::TreeBuilder->new(no_space_compacting => 1);
$html->parse($data);
$html->eof;

# This helper sub locates the first table following the given
# element ID (typically <h2 id="xxx">), then hands each of the
# rows in that table to the coderef for processing.
sub extract_table {
    my ($id, $code) = @_;
    my ($heading) = $html->look_down(id => $id);
    for($heading->right) {
        last if $_->tag =~ /^h/i;
        next unless $_->tag eq 'table';
        my $body = $_->look_down(_tag => 'tbody');
        my @output;
        for my $row ($body->look_down(_tag => 'tr')) {
            push @output, $code->($row->look_down(_tag => 'td'));
        }
        return @output;
    }
}

# This retrieves field data from tables following
# the current documentation layout - types are in bold,
# first column is field name and second is description
sub extract_fields {
    my ($id) = @_;
    return extract_table($id, sub {
        my ($field_name, $desc) = @_;
        my ($type_name) = $desc->look_down(_tag => 'strong');
        $type_name->detach;
        my %info = (
            field       => $field_name->as_text,
            type        => $type_name->as_text,
            description => ($desc->as_text =~ s{^\s*}{}r),
        );
        $log->infof('Have %s %s', $id, \%info);
        return \%info;
    });
}

# Now we process our data: first the error codes

extract_table('error-types', sub {
    my ($code, $type_description, $suggestion) = @_;
    my ($type_name) = $type_description->look_down(_tag => 'strong');
    $type_name->detach;
    my %info = (
        code => $code->as_text,
        name => $type_name->as_text,
        type_description => ($type_description->as_text =~ s{^:\s*}{}r),
        suggestion => $suggestion->as_text,
    );
    $log->infof('Have error type info %s', \%info);
    return \%info;
});

# Next the various types that can be uploaded/retrieved
my %classes = (
    Applicant => [ extract_fields('applicant-object') ],
    Address   => [ extract_fields('address') ],
    Document  => [ extract_fields('document-object') ],
    Photo     => [ extract_fields('live-photo-object') ],
    Video     => [ extract_fields('live-video-object') ],
    Check     => [ extract_fields('check-object') ],
    Report    => [ extract_fields('report-object') ],
);
extract_fields('report-type-group-object');
extract_fields('id-number');

for my $k (keys %classes) {
    my $tt = Template->new(
        ENCODING => 'utf8',
    );
    $tt->process(\q{[% -%]
package WebService::Async::Onfido::Base::[% name %];

use strict;
use warnings;

use utf8;

# VERSION

=encoding utf8

=head1 NAME

WebService::Async::Onfido::Base::[% name %] - represents data for Onfido

=head1 DESCRIPTION

This is autogenerated from the documentation in L<https://documentation.onfido.com>.

=cut

sub new {
    my ($class, %args) = @_;
    Scalar::Util::weaken($args{onfido}) if $args{onfido};
    bless \%args, $class;
}

=head1 METHODS
[% FOR field IN fields -%]
=head2 [% field.field %]

[% field.description %].

=cut

sub [% field.field %] : method { shift->{ [% field.field %] } }

[% END -%]
1;

__END__

=head1 AUTHOR

deriv.com C<< DERIV@cpan.org >>

=head1 LICENSE

Copyright deriv.com 2019. Licensed under the same terms as Perl itself.

}, {
        name   => $k,
        fields => $classes{$k}
    }, 'lib/WebService/Async/Onfido/Base/' . $k . '.pm',
        { binmode => ':encoding(UTF-8)' },
    ) or die $tt->error;
}
