#!/bin/env perl

use strict;
use warnings;
use Getopt::Long;
use Sophie::Scan;
use Sophie::Base;
use Sys::Syslog;
use Pod::Usage;

GetOptions(
    'd|daemon' => \my $daemon,
    'sleep=i' => \my $sleep,
    'runas=s' => \my $runas,
    'pidfile=s' => \my $pidfile,
) or do { pod2usage(1) };

$ENV{LC_ALL} = 'C';
require Sophie::Scan::RpmsPath;

sub process_rpms {
    my $scan = Sophie::Scan->new;
    $scan->update_meta_paths;

    my @pkey = $scan->list_unscanned_paths;

    foreach my $pathkey (@pkey) {
        
        my $time = time;
        my $mark = 0;
        my @delta;

        {
            my $path = Sophie::Scan::RpmsPath
                ->new($pathkey, Sophie::Scan->new);

            $mark = $path->get_needupdate;
            @delta = $path->find_delta;
        }

        while (my @d = splice(@delta, 0, 10)) {
            my $scan = Sophie::Scan->new;
            my $path = Sophie::Scan::RpmsPath->new($pathkey, $scan);
            $path->update_content(@d);
            last if (time > $time + 7 * 60);
        }

        if (!@delta) { # update only if we finished
            my $path = Sophie::Scan::RpmsPath->new($pathkey, Sophie::Scan->new);
            $path->set_updated;
            $path->set_no_needupdate
                unless ($mark ne $path->get_needupdate);
        }
    }
}

sub process_maint {
    my $sophie = Sophie::Base->connect;

    foreach my $maintsource (
        $sophie->resultset('MaintSources')
        ->search->all) {

        my $realclass = 'Sophie::Maint::' . $maintsource->accessor;
        no strict qw(refs);
        eval "require $realclass;";
        next if($@);

        warn "Updating maintainers for " . $maintsource->label . "\n";
        my $fetcher = $realclass->new($maintsource);
        if (my $list = $fetcher->fetch) {
            $sophie->resultset('MaintRpm')->search(
                {
                    sources => $maintsource->pkey,
                }
            )->delete;

            foreach (@$list) {
                $_->{sources} = $maintsource->pkey;
            }

            $sophie->txn_do(
                sub {
                    $sophie->resultset('MaintRpm')->populate($list) &&
                    $maintsource->update({ lastupdated => 'now()' });
                }
            );
        }
    }
}

openlog('sophie-update', 'ndelay,pid' . ($daemon ? '' : ',perror'), 'user');
$SIG{__WARN__} = sub {
    syslog('warning', @_);
};
$SIG{__DIE__} = sub {
    syslog('crit', @_);
    exit(1);
};

if ($daemon) {
    if (fork()) {
        exit(0);
    }
    warn "Fork done, entering daemon mode\n";
}

if ($pidfile) {
    if (open(my $handle, '>', $pidfile)) {
        print $handle $$ . "\n";
        close($handle);
    } else {
        die "Can't write pidfile $pidfile, exiting :\\\n";
    }
}


if ($runas) {
    my ($login,$pass,$uid,$gid) = $runas =~ /^\d/
    ? getpwuid($runas)
    : getpwnam($runas);

    $> = $uid; $) = $gid;
    if ($> ne $uid) {
        die "Cannot change to user $runas\n";
    }
}

$sleep ||= 60;

my $lastmaint = 0;

while (1) {
    my $csleep = int(rand($sleep) + ($sleep / 2 ));
    my $currenttime = time;
    if (my $pid = fork) {
        waitpid($pid, 0);
    } else {
        process_rpms();
        exit(0);
    }

    if ($lastmaint < time - ($sleep * 100)) {
        if (my $pid = fork) {
            waitpid($pid, 0);
            $lastmaint = time;
        } else {
            process_maint();
            exit(0);
        }
    }
    
    my $delay = $csleep - (time - $currenttime);

    if ($delay > 0) {
        warn "sleep $delay\n";
        sleep $delay;
    }
}
