package Common::DB::Deploy::ScriptsApplier;

use strict;
use warnings;

use Common::DB::Deploy::ChangeScript;

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

    my $self = bless {
        db_change_sripts => [],
        %args,
    }, $class;

    return $self;
}

sub logger {
    return shift->{oLogger};
}

sub commandOption {
    return shift->{oCommandOption};
}

sub versionManager {
    return shift->{oVersionManager};
}

sub databaseManager {
    return shift->versionManager->databaseManager();
}

sub getDatabaseChangeScripts {
    return shift->{db_change_sripts};
}

sub run {
    my $self = shift;

    my $databaseName = $self->databaseManager->database();
    my $hScriptsToApply = $self->versionManager->getScriptsToApply();

    $self->_check($databaseName, $hScriptsToApply) || return;
    $self->_prepare($databaseName, $hScriptsToApply);
    $self->_apply();

    return 1;
}

sub _check {
    my ($self, $databaseName, $hScriptsToApply) = @_;

    unless ( keys %$hScriptsToApply ) {
        $self->logger->debug("'$databaseName' no scripts to apply. Skip.");
        return;
    }

    return 1;
}

sub _prepare {
    my ($self, $databaseName, $hScripts) = @_;

    my @scriptsVersions = sort keys %$hScripts;
    $self->logger->info("'$databaseName' found " . (scalar @scriptsVersions) . " scripts to apply:");

    foreach my $version (@scriptsVersions) {
        my $oChangeScript = Common::DB::Deploy::ChangeScript->new(
            version   => $version,
            db_name   => $databaseName,
            file_name => $hScripts->{$version}{file},
            dir_path  => $hScripts->{$version}{dir},
        );
        push @{ $self->{db_change_sripts} }, $oChangeScript;
    }

    return 1;
}

sub _apply {
    my $self = shift;

    # if we are here, it means that there are no errors in the script files
    # let's try to apply them in one pass for the DB
    my $mode = $self->commandOption->dryrun ? ' (DRYRUN)' : '';
    foreach my $oChangeScript ( @{ $self->getDatabaseChangeScripts() } ) {
        $self->logger->info($oChangeScript->fileName . " $mode");

        foreach my $hStatement ( @{ $oChangeScript->getStatements() } ) {
            if ( $hStatement->{title} ) {
                $hStatement->{title} =~ s/\n/\n - /g;
                $self->logger->info(" - $hStatement->{title}");
            }
            next unless $hStatement->{sql};

            $self->_applyChangeScriptStatement( $hStatement->{sql} )
                unless $self->commandOption->dryrun || $self->commandOption->sync;
        }

        $self->_recordAppliedScript($oChangeScript)
            unless $self->commandOption->dryrun;
    }

    return 1;
}


sub _applyChangeScriptStatement {
    my ($self, $sql) = @_;
    return $self->databaseManager->query($sql);
}

sub _recordAppliedScript {
    my ($self, $oChangeScript) = @_;
    $self->versionManager->recordAppliedScript($oChangeScript);
    return 1;
}


1;