package LATMOS::Accounts::SyncManager;

use strict;
use warnings;
use LATMOS::Accounts::Log;
use LATMOS::Accounts;
use Config::IniFiles;

=head1 NAME

LATMOS::Accounts::SyncManager - Routine task manager

=head1 DESCRIPTION

Handle Task process run by L<la-sync-manager>

=head1 FUNCTIONS

=cut

=head2 new($config)

Instaciate object. 

C<$config> is a path to an alternative configuration file to default one.

=cut

sub new {
    my ($class, $config) = @_;

    my $ini = Config::IniFiles->new(
        -file => join('/', ($config || '/etc/latmos-accounts'),
            'la-sync-manager.ini'),
    ) or do {
        la_log LA_ERR, "Cannot load config";
        return;
    };

    foreach my $section (qw(basessynchro refreshexpired automatedrequest)) {
        if ( !grep {
            lc($ini->val($_, 'type', '')) eq $section
            } $ini->Sections ) {
            $ini->newval("_$section", 'type', $section);
        }
    }

    bless {
        ini => $ini,
        config => $config,
        lastrev => 0,
    }, $class;
}

=head2 ini

Return a reference to the L<Ini::Files> object handling configuration.

=cut

sub ini { $_[0]->{ini} }

=head2 dbrev

Return the current base revision

=cut

sub dbrev {
    my ($self) = @_;
    my $LA = LATMOS::Accounts->new($self->{config}, noacl => 1);
    my $base = $LA->base;
    return $base->current_rev;
}

sub _load_state_ini {
    my ($self) = @_;
    my $la = LATMOS::Accounts->new($self->{config}, noacl => 1);

    my $state_file =  $la->val('_default_', 'state_dir', '/');
    $state_file .= '/la-sync-manager.ini';
    la_log(LA_DEBUG, "Status file is %s", $state_file);
    if ($state_file && ! -w $state_file) {
        # don't exists, we have to create it
        open(my $handle, '>', $state_file) or do {
            la_log(LA_ERR, "Cannot open build net status file %s",
                $state_file);
            return;
        };
        print $handle "[_default_]\n";
        close($handle);
    }
    return Config::IniFiles->new(
        -file => $state_file
    );
}

=head2 get_last_rev($module)

Return the last revision the module succeed

=cut

sub get_last_rev {
    my ($self, $module) = @_;

    my $ini = $self->_load_state_ini or return;

    return ($ini->val($module, 'rev', 0), $ini->val($module, 'time', 0));
}

=head2 list_modules

List configured task module

=cut

sub list_modules {
    my ($self) = @_;
    $self->ini->Sections;
}

=head2 process_module($module)

Process C<$module>.

=cut

# TODO what is $dbrev, why is it need here ??

sub process_module {
    my ($self, $module) = @_;

    eval {
        if (!$self->ini->SectionExists($module)) {
            la_log LA_ERR, "Cannot run inexistant module %s", $module;
            return;
        }

        my $modtype = $self->ini->val($module, 'type', $module);
        my $task = LATMOS::Accounts::Task->new(
            $modtype,
            config => $self->{config},
            name   => $module,
            syncm  => $self,
        )
            or do {
            la_log(LA_ERR, 'Cannot load module %s', $modtype);
            return;
        };
        la_log LA_NOTICE, "Processing sync module %s (%s)", $module, $modtype;
        if (!$task->init) {
            la_log(LA_ERR, 'init() failed for module %s', $module);
            return;
        }

        my ($lastrev) = $self->get_last_rev($module);
        my $currentrev = $self->dbrev;
        if (!$task->needupd($lastrev, $currentrev, $self)) {
            la_log LA_DEBUG, "No change on main base, aborting";
            return 1;
        }

        my $res = $task->run;
        if (!$res) {
            my $ini = $self->_load_state_ini or return;
            $ini->newval($module, 'error', LATMOS::Accounts::Log::lastmessage());
            la_log LA_ERR, "Task %s did not end successfully", $module;
            $ini->RewriteConfig;
        } else {
            $task->post if ($res);
            my $ini = $self->_load_state_ini or return;

            $ini->delval($module, 'error');
            $ini->newval($module, 'rev', $currentrev);
            my $time = time;
            $ini->newval($module, 'time', $time);
            $ini->SetParameterComment(
                $module, 'time',
                scalar(localtime(time)),
            );
            $ini->RewriteConfig;

            la_log LA_DEBUG, "end process $module";
        }
        return $res;
    };

    if ($@) {
        la_log(LA_CRIT, 'Fatal Perl Error: %s', $@);
        return;
    } else {
        return 1;
    }
}

=head2 reset_module_savepoint($module)

Reset the savepoint for module C<$module> to force it to run at next
synchronisation.

=cut

sub reset_module_savepoint {
    my ($self, $module) = @_;
    if (!$self->ini->SectionExists($module)) {
        la_log LA_ERR, "Cannot run inexistant module %s", $module;
        return;
    }

    my $modtype = $self->ini->val($module, 'type', $module);
    my $task = LATMOS::Accounts::Task->new(
        $modtype,
        config => $self->{config},
        name   => $module,
        syncm  => $self,
    )
        or do {
        la_log(LA_ERR, 'Cannot load module %s', $modtype);
        return;
    };
    $task->init or do {
        la_log LA_ERR, "Cannot init module %s", $_;
        return;
    };

    my $ini = $self->_load_state_ini or return;

    $ini->delval($module, 'rev');
    $ini->newval($module, 'time', 0);
    $ini->SetParameterComment(
        $module, 'time',
        'Reset to 0 the ' . scalar(localtime(time)),
    );

    $task->reset_savepoint; # Specific reset
    $ini->RewriteConfig;
}

1;

__END__

=head1 SEE ALSO

L<LATMOS::Accountsi::Task>

=head1 AUTHOR

Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2012 CNRS SA/CETP/LATMOS

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.

=cut
