package Mars;

use strict;
use warnings;
use diagnostics;

use DBI;
use Net::OpenSSH;

use feature 'say';

my $local_root = '/home/christoph/Notes/TU/MastersThesis/FailNix';

my $_remote     = undef;
my $remote_host = 'mars';    # smchurla@mars.cs.tu-dortmund.de

# The mars db is bound to local port 3306 over SSH.
# - This requires using the configured 'mars'
#   remote_host instead of smchurla@mars
#   or setting up another tunnel here
my $_db          = undef;
my $db_host      = "127.0.0.1";
my $db_port      = "3306";
my $db_user      = "smchurla";
my $_db_password = undef;

sub ssh_connect {
    if ( !defined $_remote ) {

        # Initialize SSH connection
        # - This connection also sets up the database tunnel
        $_remote = Net::OpenSSH->new(
            $remote_host,
            timeout     => 30,
            master_opts => [
                -o => 'BatchMode=yes',
                -o => 'StrictHostKeyChecking=accept-new',
                -o => 'ServerAliveInterval=60',
            ],
        );
        $_remote->error and die 'SSH connection failed: ' . $_remote->error;
        say 'Connected to mars.cs.tu-dortmund.de';
    }

    return $_remote;
}

sub ssh_system {
    my (@cmd) = @_;

    my $ssh = ssh_connect();

    $ssh->system(@cmd);
    $ssh->error and die "Remote command failed (@cmd): " . $ssh->error;
}

sub ssh_capture {
    my (@cmd) = @_;

    my $ssh = ssh_connect();

    my $out = $ssh->capture(@cmd);
    $ssh->error and die "Remote command failed (@cmd): " . $ssh->error;

    return $out;
}

sub upload_dir {
    my ( $source_dir, $target_dir ) = @_;

    my $ssh = ssh_connect();

    say " - Uploading $source_dir to $target_dir...";
    $ssh->scp_put(
        {
            recursive  => 1,
            copy_attrs => 1
        },
        $source_dir,
        $target_dir,
    ) or die "Failed to upload $source_dir: " . $ssh->error;
}

sub download_dir {
    my ( $source_dir, $target_dir ) = @_;

    my $ssh = ssh_connect();

    say " - Downloading $source_dir to $target_dir...";
    $ssh->scp_get(
        {
            recursive  => 1,
            copy_attrs => 1
        },
        $source_dir,
        $target_dir,
    ) or die "Failed to download $source_dir: " . $ssh->error;
}

sub find_remote_subdirs {
    my ($dir) = @_;

    my $out = ssh_capture(
        'find',      $dir, '-mindepth', '1',
        '-maxdepth', '1',  '-type',     'd',
        '-printf',   '%f' . "\n",
    );

    my @subdirs = sort grep { length } split /\n/, $out;

    return @subdirs;
}

sub read_db_password_file {
    if ( !defined $_db_password ) {
        open( my $fhandle, '<', "$local_root/mars-db.conf" )
          or die "Failed to read mars-db.conf: $!";
        chomp( $_db_password = <$fhandle> );
        close($fhandle);
    }

    return $_db_password;
}

sub db_connect {

    # Opens tunnel for db_port
    my $ssh = ssh_connect();

    if ( !defined $_db ) {
        $_db = DBI->connect( "DBI:MariaDB:host=$db_host;port=$db_port",
            $db_user, read_db_password_file() )
          or die 'Failed to connect to database: ' . $DBI::errstr;
        say 'Connected to database';
    }

    return $_db;
}

sub db_disconnect {
    if ( defined $_db ) {
        $_db->disconnect or warn $_db->errstr;
    }
}

sub db_prefix {
    return $db_user;
}

sub db_list {
    my $db = db_connect();

    my @db_names =
      sort
      map { s/DBI:MariaDB://r }
      grep { !/information_schema|smchurla_ll/ } $db->data_sources();

    return @db_names;
}

sub db_do {
    my (@cmd) = @_;

    my $db = db_connect();

    $db->do(@cmd) or die "Database command failed (@cmd): " . $db->errstr;
}

sub db_create {
    my ($db_name) = @_;
    say " - Creating database $db_name...";
    db_do("create database `$db_name`");
}

sub db_drop {
    my ($db_name) = @_;
    say " - Dropping database $db_name...";
    db_do("drop database `$db_name`");
}

1;
