#!/usr/bin/perl

use strict;
use warnings;

use Mojo::File qw(curfile);
use lib curfile()->dirname()->sibling('lib')->to_string;

use Config::Tiny;
use English qw(-no_match_vars);
use File::Temp;
use Getopt::Long qw(:config auto_help);
use List::MoreUtils qw(uniq);
use LWP::UserAgent;
use Pod::Usage;

use AccountManager::DB;
use AccountManager::Metadata;
use AccountManager::Entity;

my %options;
GetOptions(
    \%options,
    'configuration=s',
    'verbose',
) or pod2usage(
    -message => "unknown option, aborting\n",
    -verbose => 0
);

my $configuration_file = $ENV{MOJO_CONFIG} || 'conf/manager.conf';
my $configuration = Config::Tiny->read($configuration_file);
if (!$configuration) {
    die Config::Tiny->errstr() . "\n";
}

die "no database defined in configuration, aborting\n"
    if !$configuration->{database};

die "no federation defined in configuration, aborting\n"
    if !$configuration->{federations};

AccountManager::DB->register_db(
    driver   => $configuration->{database}->{type},
    database => $configuration->{database}->{name},
    host     => $configuration->{database}->{host},
    password => $configuration->{database}->{password},
    username => $configuration->{database}->{username},
    options  => [ split(/, */, $configuration->{database}->{options}) ]
);

my $db = AccountManager::DB->new();

my $ua = LWP::UserAgent->new();

$db->begin_work();

AccountManager::Entity->delete_entities(all => 1);

$db->dbh()->do('ALTER TABLE entities AUTO_INCREMENT = 1');

my %seen;
foreach my $id (keys %{$configuration->{federations}}) {
    print "processing federation $id\n" if $options{verbose};

    my $file = File::Temp->new();
    my $url = $configuration->{federations}->{$id};
    print "downloading metadata from url $url\n" if $options{verbose};
    my $response = $ua->get($url, ':content_file' => $file->filename());
    if (!$response->is_success()) {
        $db->rollback();
        die "failed to download federation metadata: " . $response->status_line();
    }

    my $metadata;
    eval {
        $metadata = AccountManager::Metadata->new(
            file => $file
        );
    };
    if ($EVAL_ERROR) {
        $db->rollback();
        die "failed to load federation metadata: $EVAL_ERROR";
    }

    print "parsing metadata from file $file\n" if $options{verbose};
    my $entities;
    eval {
       $entities = $metadata->parse();
    };
    if ($EVAL_ERROR) {
        $db->rollback();
        die "failed to parse federation metadata: $EVAL_ERROR";
    }

    foreach my $entry (@$entities) {
        # skip entities different from SP or IdP
        next if !$entry->{type};

        my $entity;
        if ($seen{$entry->{entityid}}++) {
            $entity = AccountManager::Entity->new(
                db               => $db,
                entityid         => $entry->{entityid},
            );
            $entity->load();
            $entity->federations(
                $entity->federations(), $id
            );
            $entity->update();
        } else {
            eval {
                $entity = AccountManager::Entity->new(
                    db               => $db,
                    type             => $entry->{type},
                    entityid         => $entry->{entityid},
                    display_name     => $entry->{display_name},
                    information_url  => $entry->{information_url},
                    organization_url => $entry->{organization_url},
                );
                if ($entry->{contacts}) {
                    my @contacts =
                    uniq
                    grep { $_ }
                    map { $_->{EmailAddress} }
                    @{$entry->{contacts}};
                    $entity->contacts(@contacts);
                }
                $entity->federations($id);
                $entity->save();
            };
            if ($EVAL_ERROR) {
                warn "error while processing entity $entry->{entityid}: $EVAL_ERROR";
            }
        }

    }
}

$db->commit();

__END__

=head1 NAME

update-metadata - Out-of-band metadata processing

=head1 SYNOPSIS

update-metadata [options]

  Options:
    --configuration <file>
    --verbose