#!/usr/bin/perl -w
use strict;
use warnings;
use utf8;
use feature qw/say/;

=encoding utf8

=head1 NAME

distsync - launcher of synchronization via App::DistSync

=head1 SYNOPSIS

    distsync [options] [commands]
    distsync -D /var/www/dist init
    distsync [-dv] -D /var/www/dist status
    distsync [-d] -D /var/www/dist [-T TIMEOUT] [sync]
    distsync [-d] [-T TIMEOUT] sync /var/www/dist
    distsync [-dv] -D /var/www/dist manifest|mkmani

=head1 COMMANDS

List of commands

=over 4

=item B<init>

Initializing a new mirror in the specified directory

=item B<manifest>, B<mkmani>

MANIFEST file generation in the specified directory;
no other resource files are modified, and the directory structure is preserved

=item B<status>

Show statistic information

=item B<sync>

Synchronization of the specified directory with remote resources (mirrors)

=back

=head1 OPTIONS

=over 4

=item B<-D /path/to/directory, --dir=/path/to/directory>

Sets working directory

=item B<-d, --debug>

The I<debug> option switches operating mode to I<debug>.
In I<debug> mode all debug messages will be written to I<STDERR> by default

=item B<-H, --longhelp>

Show long help information and quit

=item B<-h, --help>

Show short help information and quit

=item B<-i secs, --delay=secs>

Max amount of seconds between retries of reading lock file (0=disabled, default is 60)

=item B<-k, --insecure>

By default, every SSL/TLS connection client makes is verified to be secure.
This option allows client to proceed and operate even for server connections
otherwise considered insecure.

The server connection is verified by making sure the server's certificate
contains the right name and verifies successfully using the cert store.

=item B<-T secs, --timeout=secs>

Set timeout for HTTP requests

=item B<-V, --version>

Print the version number of the program and quit

=item B<-v, --verbose>

Verbose mode. Show extended information on STDOUT

=item B<-x PROXY_URL, --proxy=PROXY_URL>

Sets proxy URL (http/socks4/socks5)

For example: socks://user:pass@192.168.0.1:1080

=back

=head1 DESCRIPTION

Launcher of synchronization via App::DistSync

See C<README> file for details

=head1 AUTHOR

Serż Minus (Sergey Lepenkov) L<https://www.serzik.com> E<lt>abalama@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 1998-2025 D&D Corporation. All Rights Reserved

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See L<https://dev.perl.org/licenses/>

=cut

use Getopt::Long;
use Pod::Usage;

use Cwd qw/getcwd/;
use File::Spec;

use App::DistSync;
use App::DistSync::LockFile;
use App::DistSync::Util;

binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';


use constant {
    PIDFILE     => 'distsync.pid',
    CMDDEFAULT  => 'sync',
    COMMANDS    => [qw/init status sync manifest mkmani/],
};

$| = 1;  # Autoflush on

my $options = {};
Getopt::Long::Configure ("bundling");
GetOptions($options,
    # Information and debug
    "help|usage|h",         # Show help page
    "longhelp|H|?",         # Show long help page
    "debug|devel|d",        # Debug mode
    "verbose|v",            # Verbose mode
    "version|ver|V",        # Print VERSION of the project

    # Client options
    "proxy|x=s",            # Proxy URL
    "insecure|k",           # Insecure connection
    "timeout|T=i",          # Timeoute value in seconds. Default - 60

    # Project
    "directory|dir|D=s",    # Directory
    "delay|i=i",            # Delay. 0 - off, 1-n - secs, default - 3600
) || pod2usage(-exitval => 1, -verbose => 0, -output => \*STDERR);
pod2usage(-exitval => 0, -verbose => 1) if $options->{help};
pod2usage(-exitval => 0, -verbose => 2) if $options->{longhelp};
printf("DistSync v%s\n", App::DistSync->VERSION) && exit(0) if $options->{version};

# Get command
my $command   = @ARGV ? shift @ARGV : CMDDEFAULT;
pod2usage(-exitval => 1, -verbose => 99, -sections => 'SYNOPSIS|OPTIONS|COMMANDS', -output => \*STDERR)
    unless scalar grep {$_ eq $command} @{(COMMANDS)};

# Work directory
my $dir = $options->{directory} || shift(@ARGV) || getcwd();
die sprintf("Directory \"%s\" not exists", $dir) unless $dir && (-e $dir) && (-d $dir or -l $dir);

# Debugging on
$App::DistSync::Util::DEBUG = $App::DistSync::DEBUG = 1 if $options->{debug};

# Start process
my $started = time;
debug("%s START", tms);

# Lock file
my $lock = App::DistSync::LockFile->new(
        file => File::Spec->catfile($dir, App::DistSync::MANILOCK()),
        $options->{delay} ? (delay => $options->{delay}) : (),
        pid => $$,
    );
if ($lock->lock->error) {
    warn $lock->error . "\n";
    goto FINISH;
}

# Create instance
my $ds = App::DistSync->new(
    dir     => $dir,
    pid     => $$,
    timeout => $options->{timeout},
    verbose => $options->{verbose},
    insecure=> $options->{insecure},
    proxy   => $options->{proxy},
);

my $exitval = 1; # error

# Init
if ($command eq 'init') {
    $ds->init or goto FINISH;
    say "The work directory has been successfully initialized";
    printf "Your files are in \"%s\"\n", $ds->dir if $options->{verbose};
    $exitval = 0; # Ok
}

# Status
elsif ($command eq 'status') {
    if ($ds->status) {
        $ds->_show_summary;
        $exitval = 0; # Ok
    }
}

# Sync
elsif ($command eq 'sync') {
    if ($ds->sync) {
        say "Sync successfully completed";
        goto FINISH unless $options->{verbose};
        $ds->_show_summary;
        $exitval = 0; # Ok
    } else {
        printf("Sync \"%s\" completed with errors\n", $ds->dir);
    }
}

# Make manifest file
elsif ($command eq 'manifest' or $command eq 'mkmani') {
    if ($ds->mkmani) {
        say sprintf "The MANIFEST file has been successfully generated in \"%s\"", $ds->dir;
        $exitval = 0; # Ok
    } else {
        printf("Failed to generate MANIFEST file in \"%s\"\n", $ds->dir);
    }
}

FINISH: debug("%s FINISH", tms);
warn $lock->error . "\n" if $lock->unlock->error;
exit $exitval;

1;

__END__


