#!/usr/bin/perl
use strict;
use File::Basename qw/ dirname basename /;
use File::Spec;
use Cwd qw/abs_path/;

our @INC;

BEGIN {
    push @INC, File::Spec->catdir(dirname(abs_path($0)), 'lib');
}

use utf8;
use v5.10;

use VolumeManager;
use Compose;

use Mounts;
use Log::Log4perl;
use DateTime;
use Pod::Usage;
use Getopt::Long;
use YAML::XS qw(LoadFile);
use File::Temp qw/tempfile/;

binmode *STDOUT, ':utf8';

my $cfgfile = "/etc/esntd-tu/esntd-tu-admin.yml";
my $debug;

if (($ARGV[0] || '') eq '--conf') {
    shift @ARGV;
    $cfgfile = shift @ARGV;
}

my $config = LoadFile($cfgfile);

die "The configuration property '$_' must be specified"
    foreach grep not($config->{$_}), qw(vg lv mnt compose);
    
my $level = $config->{debug} ? 'DEBUG' : 'ERROR';
    
my $logcnf = qq(
    log4perl.rootLogger = $level, Screen
    log4perl.appender.Screen = Log::Log4perl::Appender::Screen
    log4perl.appender.Screen.stderr = 0
    log4perl.appender.Screen.layout = Log::Log4perl::Layout::PatternLayout
	log4perl.appender.Screen.layout.ConversionPattern =%c - %m (%R ms)%n
);

Log::Log4perl->init( \$logcnf );

my ($vm,$compose);

sub GetManager() {
    unless($vm) {
        $vm = VolumeManager->new($config->{lv}, $config->{vg}, $config->{mnt});
    }
    return $vm;
}

sub GetCompose() {
    unless($compose) {
        $compose = Compose->new($config->{compose});
    }
    return $compose;
}

my $action = lc(shift @ARGV || 'help');
$action =~ s/^--//;

my %actions = (
    help => \&PrintUsage,
    backup => \&Backup,
    'backup-list' => \&BackupList,
    'backup-restore' => \&BackupRestore,
    start => \&Start,
    stop => \&Stop,
    restart => \&Restart,
    status => \&Status,
    'backup-remove' => \&BackupRemove
);

($actions{$action} || \&UnknownAction)->();


sub UnknownAction {
    say "Uknown operation '$action' specified";
    PrintUsage();
}

sub PrintUsage {
    pod2usage(-verbose => 0);
}

sub Backup {
    die "Nothing to backup" unless GetManager->tip();

    say "Остановка системы";
    GetCompose->stop();

    say "Создание резервной копии";
    GetManager->takeSnapshot();
    
    say "Запуск системы";
    GetCompose->start();
    say "Система запущена";
}

sub BackupList {
    my @snaps = sort { $b->cTime cmp $a->cTime } GetManager->snapshots();
    if(@snaps) {
        say "Резервные копии:";
        say "\t- ", $_->volumeName," (", $_->cTime ,")" foreach @snaps;
    } else {
        say "Нет резервных копий";
    }
        
}

sub BackupRestore {
    my $name = shift @ARGV;

    unless ($name) {
        my ($snap) = sort { $b->cTime cmp $a->cTime }
            GetManager->snapshots();
        die "There are no backups in the system" unless $snap;
        $name = $snap->volumeName;
    }

    say "Восстановление резервной копии '$name'";

    say "Остановка системы";
    GetCompose->stop();    

    say "Восстановление данных";
    GetManager->switch($name, unmount => 1);

    say "Запуск системы";
    GetCompose->start();
    say "Система запущена";
}

sub BackupRemove {
    my ($name, $time);
    GetOptions(
        'name|n=s' => \$name,
        'time|t=i' => \$time
    );

    die "A -n or -t must be specified" unless ($name or defined $time);

    if ($name) {
        GetManager->removeSnaphot($name);
        say "Резераная копия $name удалена";
    } elsif (defined $time) {
        my $cnt = GetManager->pruneSnaphots($time);
        say "Резервные копии старше ${time}д удалены: $cnt шт.";
    }
}

sub Start {
    say "Запуск системы";
    GetCompose->start();
    say "Система запущена";
}

sub Stop {
    say "Остановка системы";
    GetCompose->stop();
    say "Система остановлена";
}

sub Restart {
    say "Перезапуск системы";
    GetCompose->restart();
    say "Система запущена";
}

sub Status {
    my @errors = GetCompose->check();

    if(@errors) {
        say "Обнаружены ошибки";
        say "- $_" foreach @errors;
        exit(1);
    }
    say "Ошибок не обнаружено";
}

1;

__END__

=pod

=head1 NAME

C<esntd-tu-admin> - Утилита для управления системой

=head1 SYNOPSIS

=begin

esntd-tu-admin [operation] [options]

    --install {archive}
        Устанавливает систему из архива с дистрибутивом, пример названия архива
        esntd-tu.app.<версия>.tar.xz

    --update {archive}
        Обновляет систему из архива с дистрибутивом, пример названия архива
        esntd-tu.app.<версия>.tar.xz

    --uninstall
        Удаляет систему.

    --backup
        Создает резерную копию данных системы.

    --backup-list
        Выводит список резерных копий.

    --backup-restore [name]
        Восстанавливает данные сиситемы из резервной копии.

    --backup-remove [-{n,t}]
        Удаляет снимки
        
        -n {имя снимка}
            Удалить снимок с указанным именем
        -t {n}
            Удалить снимки старше {n} дней
    
    --restart
        Перезапускает систему.
    
    --status
        Выводит состояние системы

    --stop
        Останавливает систему

    --start
        Запускает систему

    --help
        Текущая справка

=end

=head1 DEPENDENCIES

=over 4

=item * File::Spec

=item * Log::Log4perl

=item * DateTime

=item * DateTime::Format::Strptime

=item * JSON::XS

=item * Cwd

=item * Pod::Usage

=item * File::Temp

=item * Getopt::Long

=back

=cut