#!/usr/bin/perl

BEGIN { die "Do not run this as root" unless $>; }

use strict;
use warnings;
use utf8;
use AmuseWikiFarm::Schema;
use AmuseWikiFarm::Utils::Jobber;
use AmuseWikiFarm::Log::Contextual;
use Try::Tiny;
use Getopt::Long;
use AmuseWikiFarm::Utils::CgitSetup;
use AmuseWikiFarm::Utils::Amuse;

# load the locales, in case it's needed
AmuseWikiFarm::Utils::Amuse::load_all_datetime_locales();


my $max_parallel = 3;
# legacy
my $polling_interval = $ENV{AMW_POLLING} || 5;

GetOptions('max-parallel=i' => \$max_parallel,
           'polling-interval=i' => \$polling_interval)
  or die;

=pod

=head1 NAME

amusewiki-jobber - amusewiki job daemon

=head1 SYNOPSIS

amusewiki-jobber [ --max-parallel 3 ] [ --polling-interval 5 ]

Daemon which takes care of all the slow amusewiki's operations. Needs
to be launched from the application's root, where the repo are located.

It stays in the foreground, but each job is managed in a double fork
(so killing it doesn't kill the grandchild).

You can set the maximum of parallel job and the polling interval via
option.

=cut

my $schema = AmuseWikiFarm::Schema->connect('amuse');

my $max_retry = 30;
while (!$schema->storage->ensure_connected and $max_retry) {
    $max_retry--;
    sleep 10;
}

unless ($schema->storage->ensure_connected) {
    log_error { "Couldn't connect to DB after waiting 5 minutes" };
    die;
}

# at startup, ensure all the sites will have fresh indexes. This will
# schedule the jobs. Wrapped in eval if DB is failing us.
AmuseWikiFarm::Utils::CgitSetup->new(schema => $schema)->configure;
try {
    foreach my $site ($schema->resultset('Site')->all) {
        $site->initialize_remote_repo;
    }
    AmuseWikiFarm::Utils::CgitSetup->new(schema => $schema)->configure;
} catch {
    my $err = $_;
    log_error { "Error initializing the remote git repositories" };
};

foreach my $site ($schema->resultset('Site')->all) {
    try {
        $site->check_and_update_custom_formats;
        $site->index_site_files;
        $site->generate_static_indexes;

        # ignore the attachments in the staging area instead of dying
        # because out of the root
        my $attachments = $site->attachments->search({
                                                      f_archive_rel_path => { '!=' => '' },
                                                      errors => undef,
                                                     });
        while (my $att = $attachments->next) {
            if ($att->has_thumbnails and !$att->thumbnails->count) {
                log_info { "Generating thumbnails for " . $att->uri };
                try {
                    $att->generate_thumbnails;
                } catch {
                    my $err = $_;
                    log_error { $site->id . ": error generating thumbnail: $err" };
                };
            }
        }
    }
    catch {
        my $err = $_;
        log_error { "Error at startup: $err" };
    };
}

# check if there are missing checksums
try {
    foreach my $class (qw/Title Attachment/) {
        foreach my $obj ($schema->resultset($class)->search({
                                                               'mirror_info.mirror_info_id' => undef,
                                                              },
                                                              {
                                                               join => 'mirror_info',
                                                              }
                                                             )->all) {
            $obj->create_related(mirror_info => { site_id => $obj->site_id })->discard_changes->compute_checksum;
        }
    }
} catch {
    my $err = $_;
    log_error { "Error while checking the mirror info: $err" };
};

# double fork and upgrade xapian if needed

XAPIAN_UPGRADE: {
    if (my $child = fork()) {
        log_debug { "Main $$ Waiting for $child" };
        wait;
    }
    elsif (defined $child) {
        if (my $grandchild = fork()) {
            # here we exit the child and the grandchild is detached.
            log_debug { "Exiting $$" };
            exit;
        }
        elsif (defined $grandchild) {
            log_info {
                "Detached and upgrading xapian with pid $$\n";
            };
            foreach my $site ($schema->resultset('Site')->all) {
                unless ($site->xapian->database_is_up_to_date) {
                    log_info { "Upgrading Xapian DB for " . $site->id };
                    my $time = time();
                    $site->xapian_reindex_all;
                    log_info { "Rebuilt Xapian DB in " . (time() - $time) . " seconds\n" } ;
                }
            }
            log_info { "Exiting $$" };
            exit;
        }
        else {
            die "Couldn't fork child $!";
        }
    }
    else {
        die "Couldn't fork $!";
    }
}

my $jobber = AmuseWikiFarm::Utils::Jobber->new(
                                               schema => $schema,
                                               polling_interval => $polling_interval,
                                               max_parallel => $max_parallel,
                                              );

print "Starting jobber's loop with polling interval of " . $jobber->polling_interval . " seconds with pid $$\n";
print "Lockfile is " . $jobber->pidfile . "\n";
print "Max parallel jobs: " . $jobber->max_parallel . "\n";

while (1) {
    try {
        $jobber->main_loop;
    } catch {
        my $err = $_;
        log_error { "Error $err on the jobber's main loop" };
    };
}
    

