package Common::Deploy::Process;

use lib '/app/tools/common/lib';
use Common::RSApp;

use Data::Dumper;

use Common::Config;

use Common::Deploy::Development;
use Common::Deploy::Staging;
use Common::Deploy::Production;

use base 'Common::Script';

sub _common_options {
    my $self = shift;

    my $options = $self->_valid_options();
    my $result;

    foreach my $group ( keys(%$options) ) {
        foreach my $option ( keys( %{ $options->{$group} } ) ) {
            $result->{$option} = $options->{$group}->{$option};
        }
    }

    return $result;
}

sub _valid_options {
    return {
        global => {
            list => {
                short       => 'l',
                description => 'List all checkout branches and releases',
            },
            verbose => {
                short       => 'v',
                description => 'Display verbose output'
            },
            debug => {
                short       => 'd',
                description => 'Display debug output'
            },
            help => {
                short       => 'h',
                description => 'display this screen'
            },
            force => {
                description => 'force an action'
            },
            production => {
                short       => 'p',
                description => 'Perform task on the production servers'
            },
            status => {
                description => 'Return the status of the current branch.  Optional parameter, branch_name',
                parameter   => ":s",
            },
            list_branches => {
                description => 'List all available branches'
            },
            list_releases => {
                description => 'List all available releases'
            },
            ignore => {
                description => 'File to ignore during merge',
                parameter   => 's',
            },
            include => {
                description => 'File to explicitly include during merge',
                parameter   => 's',
            },
            ignore_file => {
                description => 'File containing a list of files to ignore during merge',
                parameter   => "s",
            },
            include_file => {
                description => 'File containing a list of files to explicitly include during merge',
                parameter   => "s",
            },
        },

        production => {
            host => {
                description => 'Limit action to specific production host',
                parameter   => "s"
            },
            push => {
                description => 'Push code to production servers (default to the latest release)',
                parameter   => ":s",
            },
        },

        development => {
            create_branch => {
                description => 'Create new development branch',
                parameter   => "s"
            },
            remove => {
                description => 'Remove a local copy of a branch or release',
                parameter   => "s"
            },
            checkout => {
                description => 'Checkout a branch',
                parameter   => "s"
            },
            merge => {
                description => 'Merge in changes from the trunk',
                parameter   => ":s"
            },
        },

        staging => {
            remove => {
                description => 'Remove a local copy of a branch or release',
                parameter   => "s"
            },
            create_release => {
                description => 'Create new release branch (branch name is optional)',
                parameter   => ":s"
            },
            checkout => {
                description => 'Checkout a branch',
                parameter   => "s"
            },
            merge => {
                description => 'Merge in changes from the trunk',
                parameter   => ":s"
            },
        }
    };
}

sub start {
    my $class = shift;
    my $self = ref($class) ? $class : $class->new(@_);

    $self->_parse_options();

    my $app = new Common::RSApp( clientID => 0 );

    Common::Deploy->validateDirectoryStructure();

    if ( $self->param('help') ) {
        $self->usage();
    } elsif ( $self->param('list') ) {
        Common::Deploy->list();
    } else {
        $self->run();
    }
}

sub run {
    my $self = shift;

    my $processType = $self->_processType();

    $self->debug("Starting process on a **$processType** server");

    $self->_validateParameters($processType);

    my $deploy = $self->_getDeploymentObject($processType);

    my $error = $deploy->run();

    $self->usage($error) if ($error);
}

sub _processType {
    my $self = shift;

    # This shouldn't be run on production servers
    if (Common::RSApp::IsProductionServer) {
        die "Script should be run on staging or development servers";

        # Staging is used to push code and to stage code.
    } elsif (Common::RSApp::IsStagingServer) {
        if ( $self->param('production') ) {
            return 'production';
        } else {
            return 'staging';
        }

        # We are working with development
    } else {
        return 'production' if ( $self->param('production') && $self->param('force') );

        die "Production tasks can only be initiated on staging servers (use --force to override)\n"
          if ( $self->param('production') && !$self->param('force') );

        return 'development';
    }

    die "We should never be able to get here";
}

sub _getDeploymentObject {
    my $self        = shift;
    my $processType = shift || die "Process type required";
    my $options     = $self->_valid_options();

    $self->debug("Creating deployment object");

    foreach my $option ( keys( %{ $options->{global} } ), keys( %{ $options->{$processType} } ) ) {
        $args{$option} = $self->param($option);
    }

    $args{process_type} = $processType;

    if ( $processType eq 'production' ) {
        return new Common::Deploy::Production(%args);
    } elsif ( $processType eq 'staging' ) {
        return new Common::Deploy::Staging(%args);
    } elsif ( $processType eq 'development' ) {
        return new Common::Deploy::Development(%args);
    } else {
        die "Unknown process type";
    }
}

sub _validateParameters {
    my $self = shift;
    my $processType = shift || die;
    my @invalid;
    my %validOptions;
    my %allOptions;

    my $options = $self->_valid_options();

    foreach my $group ( keys(%$options) ) {
        foreach my $option ( keys( %{ $options->{$group} } ) ) {
            $allOptions{$option} = 1;
            $validOptions{$option} = 1 if ( $group =~ /^(global|$processType)$/ );
        }
    }

    foreach my $option ( keys(%allOptions) ) {
        push( @invalid, $option ) if ( defined( $self->param($option) ) && !$validOptions{$option} );
    }

    $self->usage( "ERROR: Options not valid for $processType: " . join( ', ', @invalid ) ) if ( @invalid && !$self->{_force} );
}

1;
