# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --

package Kernel::System::Console::Command::Dev::Tools::Database::XML2SQL;

use strict;
use warnings;

use parent qw(Kernel::System::Console::BaseCommand);

## nofilter(TidyAll::Plugin::OTRS::Perl::ObjectManagerCreation)

our @ObjectDependencies = (
    'Kernel::Config',
    'Kernel::System::DB',
    'Kernel::System::Main',
    'Kernel::System::XML',
);

sub Configure {
    my ( $Self, %Param ) = @_;

    $Self->Description('Convert OTRS database XML to SQL.');
    $Self->AddOption(
        Name        => 'database-type',
        Description => "Specify the database to generate SQL for (mysql|postgresql|oracle|all).",
        Required    => 1,
        HasValue    => 1,
        ValueRegex  => qr/^(mysql|postgresql|oracle|all)$/smx,
    );
    $Self->AddOption(
        Name        => 'source-path',
        Description => "Read XML from the specified file (otherwise STDIN will be used).",
        Required    => 0,
        HasValue    => 1,
        ValueRegex  => qr/.*/smx,
    );
    $Self->AddOption(
        Name        => 'target-directory',
        Description => "Specify the output directory (otherwise the result will be printed on the console).",
        Required    => 0,
        HasValue    => 1,
        ValueRegex  => qr/.*/smx,
    );
    $Self->AddOption(
        Name        => 'target-filename',
        Description => "Specify the output filename.",
        Required    => 0,
        HasValue    => 1,
        ValueRegex  => qr/.*/smx,
    );
    $Self->AddOption(
        Name        => 'split-files',
        Description => "Split foreign key creation into a separate (post) SQL file.",
        Required    => 0,
        HasValue    => 0,
    );

    return;
}

sub PreRun {
    my ( $Self, %Param ) = @_;

    my $TargetDirectory = $Self->GetOption('target-directory');
    if ($TargetDirectory) {
        if ( !-d $TargetDirectory ) {
            die "Directory $TargetDirectory does not exist.\n";
        }
        my $TargetFilename = $Self->GetOption('target-filename');
        if ( !$TargetFilename ) {
            die "Please provide the option 'target-filename'.\n";
        }
    }
    my $SourceFilename = $Self->GetOption('source-path');
    if ( $SourceFilename && !-r $SourceFilename ) {
        die "Source file $SourceFilename does not exist / cannot be read.\n";
    }

    return;
}

sub Run {
    my ( $Self, %Param ) = @_;

    my @DatabaseType = ( $Self->GetOption('database-type') );
    if ( $Self->GetOption('database-type') eq 'all' ) {
        @DatabaseType = qw(mysql postgresql oracle);
    }

    my $SourceFilename = $Self->GetOption('source-path');
    my $SourceXML;
    if ($SourceFilename) {
        my $FileStringRef = $Kernel::OM->Get('Kernel::System::Main')->FileRead(
            Location        => $SourceFilename,
            Mode            => 'utf8',
            Type            => 'Local',
            Result          => 'SCALAR',
            DisableWarnings => 1,
        );

        $SourceXML = ${$FileStringRef};
    }
    else {
        # read xml data from STDIN
        $SourceXML = do { local $/; <> };
    }

    for my $DatabaseType (@DatabaseType) {

        local $Kernel::OM = Kernel::System::ObjectManager->new();

        $Kernel::OM->Get('Kernel::Config')->Set(
            Key   => 'Database::Type',
            Value => $DatabaseType,
        );
        $Kernel::OM->Get('Kernel::Config')->Set(
            Key   => 'Database::ShellOutput',
            Value => 1,
        );

        # parse xml package
        my @XMLARRAY = $Kernel::OM->Get('Kernel::System::XML')->XMLParse( String => $SourceXML );

        my $DBObject = $Kernel::OM->Get('Kernel::System::DB');

        my $Head = $DBObject->{Backend}->{'DB::Comment'}
            . "----------------------------------------------------------\n";
        $Head .= $DBObject->{Backend}->{'DB::Comment'}
            . " driver: $DatabaseType\n";
        $Head .= $DBObject->{Backend}->{'DB::Comment'}
            . "----------------------------------------------------------\n";

        # get sql from parsed xml
        my @SQL;
        if ( $DBObject->{Backend}->{'DB::ShellConnect'} ) {
            push @SQL, $DBObject->{Backend}->{'DB::ShellConnect'};
        }
        push @SQL, $DBObject->SQLProcessor( Database => \@XMLARRAY );

        # get port sql from parsed xml
        my @SQLPost;
        if ( $DBObject->{Backend}->{'DB::ShellConnect'} ) {
            push @SQLPost, $DBObject->{Backend}->{'DB::ShellConnect'};
        }
        push @SQLPost, $DBObject->SQLProcessorPost();

        my $TargetFilename     = $Self->GetOption('target-filename');
        my $TargetFilenamePost = $Self->GetOption('target-filename');
        if ($TargetFilename) {
            $TargetFilename     = $Self->GetOption('target-directory') . "/$TargetFilename.$DatabaseType.sql";
            $TargetFilenamePost = $Self->GetOption('target-directory') . "/$TargetFilenamePost-post.$DatabaseType.sql";
        }

        if ( $Self->GetOption('split-files') ) {

            # write create script
            $Self->Dump(
                $TargetFilename,
                \@SQL,
                $Head,
                $DBObject->{Backend}->{'DB::ShellCommit'},
            );

            # write post script
            $Self->Dump(
                $TargetFilenamePost,
                \@SQLPost,
                $Head,
                $DBObject->{Backend}->{'DB::ShellCommit'},
            );
        }
        else {
            $Self->Dump(
                $TargetFilename,
                [ @SQL, @SQLPost ],
                $Head,
                $DBObject->{Backend}->{'DB::ShellCommit'},
            );
        }
    }
    return $Self->ExitCodeOk();
}

sub Dump {
    my ( $Self, $Filename, $SQL, $Head, $Commit ) = @_;

    if ($Filename) {

        my $Content = $Head;
        for my $Item ( @{$SQL} ) {
            $Content .= $Item . $Commit . "\n";
        }

        $Self->Print("Writing: <yellow>$Filename</yellow>\n");

        my $Written = $Kernel::OM->Get('Kernel::System::Main')->FileWrite(
            Location => $Filename,
            Content  => \$Content,
            Mode     => 'utf8',
            Type     => 'Local',
        );

        if ( !$Written ) {
            $Self->PrintError("Could not write $Filename.");
        }
    }
    else {
        print $Head;
        for my $Item ( @{$SQL} ) {
            print $Item . $Commit . "\n";
        }
    }

    return 1;
}

1;
