package Common::Deploy::Production;

use lib '/app/tools/common/lib';

use Common::Deploy::Info;
use Data::Dumper;

use base 'Common::Deploy';

sub _validActions { ( shift->SUPER::_validActions(), 'push' ) }

sub _push {
    my $self    = shift;
    my $release = $self->_getRelease(shift);
    my @hosts   = $self->_productionHosts();

    print "Pushing release '$release' to hosts " . join( ", ", @hosts ) . "\n";

    $self->_validateBranch($release);
    $self->_checkLocalStatus($release);
    $self->_checkStatus($release);
    $self->_pushUpdate($release);
}

sub _getRelease {
    my $self    = shift;
    my $release = shift;

    if ($release) {
        die "Release '$release' does not exist" unless ( Common::Deploy::Info->releaseExist($release) );
    } else {
        my @r = Common::Deploy::Info->releases();
        die "No releases found" unless (@r);

        $release = $r[ @r - 1 ]->{name};
    }

    return $release;
}

sub _productionHosts {
    my $self            = shift;
    my @productionHosts = Common::RSApp::GetProductionServers();

    if ( $self->{_host} ) {
        return ( $self->{_host} ) if ( $self->{_force} || grep( /^$self->{_host}$/, @productionHosts ) );

        die "'$self->{_host}' is not a production server. (use --force to override)\n";
    } else {
        return @productionHosts;
    }
}

sub _validateBranch {
    my $self = shift;
    my $release = shift || die;

    die "ERROR: Can not push trunk live.\n" if ( $release eq 'trunk' );
}

sub _checkLocalStatus {
    my $self       = shift;
    my $release    = shift || die;
    my $repository = $self->repository();

    print " - Checking status of the local branch\n";

    my $status = $self->_getCVSStatus("$repository/$release/code");

    die "Local repository '$repository/$release' is not Up-to-date." if ($status);
}

sub _checkStatus {
    my $self = shift;
    my $release = shift || die;
    my $conficts;
    my $modified;
    my $unknown;
    my %status;
    my $summary;

    print " - Checking status of production servers\n";

    foreach my $host ( $self->_productionHosts() ) {
        print "   + getting status for $host\n";
        $status{$host} = $self->_getHostStatus( $host, $release );

        $modified  = 1 if ( grep( /^M /,  split( /\n/, $status{$host} ) ) );
        $conflicts = 1 if ( grep( /^C /,  split( /\n/, $status{$host} ) ) );
        $unknown   = 1 if ( grep( /^\? /, split( /\n/, $status{$host} ) ) );
    }

    $summary = $self->_summarizeStatus(%status);

    print "Update Summary: \n";

    for my $host ( keys(%$summary) ) {
        print "---------------------------------------------------------------\n";
        print " host(s): $host\n";
        print $summary->{$host} . "\n";
    }

    die "\nERROR: Conflicts exist which must be fixed before a release can be pushed live.\n"                        if ($conflicts);
    die "\nERROR: Unknown files exist on the destination hosts which must be fixed before a release can be pushed\n" if ($unknown);
    die "\nERROR: Locally modified files exist on the destination hosts which must be fixed before a release can be pushed\n"
      if ($modified);
}

sub _getHostStatus {
    my $self    = shift;
    my $host    = shift || die;
    my $release = shift || die;

    my $cmd = "ssh $host.royaltyshare.com 'cd /app/tools && cvs -q -n up -P -r $release 2>&1'";
    Common::Log::Debug("Execute: $cmd");

    my $output = `$cmd`;

    ## CVS give a failed exit status on conflicts, so we had to put an exception in for that.
    die "Command Failed\n" . "Command: $cmd\n" . "Status: $?\nOutput: $output" if ( $? && !grep( /^C /, split( /\n/, $output ) ) );

    Common::Log::Debug("Result: $output");

    return $output;
}

sub _summarizeStatus {
    my $self   = shift;
    my %status = @_;
    my %flip;
    my %result;
    my @lines;

    foreach my $host ( keys(%status) ) {
        my $key = join( "\n", grep( /^[\?CMUP] /, split( /\n/, $status{$host} ) ) );

        $flip{$key} = [] unless ( $flip{$key} );
        push( @{ $flip{$key} }, $host );
    }

    foreach my $data ( keys(%flip) ) {
        my $hosts = join( ",", @{ $flip{$data} } );
        $result{$hosts} = $data;
    }

    return \%result;
}

sub _pushUpdate {
    my $self    = shift;
    my $release = shift;

    print " - Pushing code to production servers\n";

    foreach my $host ( $self->_productionHosts() ) {
        $self->_pushToHost( $host, $release );
    }

    Common::Deploy::Info->markRelease($release);
}

sub _pushToHost {
    my $self    = shift;
    my $host    = shift;
    my $release = shift;

    print "   + pushing code to $host\n";
    my $cmd = "ssh $host.royaltyshare.com 'cd /app/tools && cvs -q up -P -r $release 2>&1'";
    Common::Log::Debug("Execute: $cmd");

    die "aborted\n" unless ( $self->_promptYesNo("Ready to push changes live? (yes/no)") );

    my $output = `$cmd`;

    ## CVS give a failed exit status on conflicts, so we had to put an exception in for that.
    die "Command Failed\n" . "Command: $cmd\n" . "Status: $?\nOutput: $output" if ($?);

    Common::Log::Debug("Result: $output");

    $self->_postProcess($host);
}

sub _postProcess {
    my $self = shift;
    my $host = shift;

    print "   + running post process on $host\n";

}

1;
