#!/usr/bin/perl -Ilibs

package API::SiteExperimentAdmin;

use strict;
use warnings;
use autodie;
use namespace::autoclean;

use Carp;
use Moose;
use Try::Tiny;

extends "API::Controller";

use API::Config;

use ECS::SiteExperiment;

__PACKAGE__->meta->make_immutable;

###############################################################################

sub index {
    my $self = shift->new({'input' => shift || {}});

    $self->data->{'error'} = $self->input->{'error'}
        if $self->input->{'error'};

    $self->data->{'templates'} = [ "index.html" ];

    return $self->result;
}

###############################################################################

sub saveChanges {
    my $self = shift->new({ 'input' => shift || {} });

    # parse the input looking for any columns
    my %experimentSaveData;
    my %casesByCol = ();
    my %casesById = ();
    for my $field (keys %{ $self->input }) {
        if ($field =~ m/^experiment_(\w+)_(\d+)$/) {
            my ( $col, $id ) = ( $1, $2 );
            $experimentSaveData { $id }->{ $col } = $self->input->{ $field };
            next; # handled
        }
        next unless $field =~ m/^case_(\w+)_(\d+)$/;
        my ( $col, $id ) = ( $1, $2 );
        $casesByCol{ $col }->{ $id } = $self->input->{ $field };
        $casesById{ $id }->{ $col } = $self->input->{ $field };
    }

    if (keys %experimentSaveData > 0) {

        if (not $self->_validateExperimentSaveData({
            'saveData' => \%experimentSaveData,
        })) {
            my $errMsg = "Site Experiments out of date. Page was refreshed. Retry Change and Save";
            $self->data->{'redirect'} = "./?error=$errMsg";
            return $self->result;
        }

        $self->_saveExperiments({
            'saveData' => \%experimentSaveData,
        });

    }

    my @rows = sort keys %casesById;
    my @cols = sort ( keys %casesByCol, "siteExperimentCaseID" );

    if(@rows) {
        my $sqlCols = join ", ", @cols;
        my @rowSQL = (join ", ", ("?") x @cols ) x @rows;
        my $sqlPlh = join ", ", map { "( $_ )" } @rowSQL;
        my @updateCols = grep { ! m/^siteExperimentCaseID$/ } @cols;
        my $sqlUpd = join ", ", map { "$_ = VALUES( $_ )" } @updateCols;
        my $sql = qq{
            INSERT INTO siteExperimentCase
                ( $sqlCols )
            VALUES
                $sqlPlh
            ON DUPLICATE KEY UPDATE
                $sqlUpd
        };
        my $sth = $self->dbh->prepare( $sql );

        my @values = ();
        for my $id (keys %casesById) {
            push @values, $id;

            my $case = $casesById{ $id };
            for my $col (sort keys %{ $case }) {
                push @values, $case->{ $col };
            }
        }

        $sth->execute( @values );
    }

    $self->data->{'redirect'} = ".";
    return $self->result;
}

###############################################################################

sub _validateExperimentSaveData {
    my $self = shift;
    my ($p) = @_;

    my $saveData = $p->{'saveData'}
        or croak "saveData required";

    my @saveDataExperimentIDs = keys %{ $saveData };
    my $schema = ECS::DB::Schema::getSchema({
        'dbh' => $self->dbh,
        'schema' => "SiteExperiment",
    });
    my $rs = $schema->search(
        {
            'siteExperimentID' => \@saveDataExperimentIDs,
        },
    );

    my @existingExperimentIDs;
    while (my $experiment = $rs->next) {
        push(@existingExperimentIDs, $experiment->siteExperimentID);
    }

    # Valid if all save data siteExperimentIDs were found in the database
    return @saveDataExperimentIDs == @existingExperimentIDs;
}

###############################################################################

sub _saveExperiments {
    my $self = shift;
    my ($p) = @_;

    my $saveData = $p->{'saveData'}
        or croak "saveData required";

    my @prepares;
    my @values;
    for my $experimentID (keys %{ $saveData }) {
        push @prepares, "(?,?)";
        push @values, $experimentID,
            $saveData->{ $experimentID }->{'gaDimIdx'} || undef,
    }

    my $sth = $self->dbh->prepare(
        "INSERT INTO siteExperiment (
            siteExperimentID,
            gaDimIdx
        ) VALUES ".join(',', @prepares)."
        ON DUPLICATE KEY UPDATE
            gaDimIdx = VALUES(gaDimIdx)
        "
    );

    $sth->execute( @values );

    return;
}

###############################################################################

sub flushTraffic {
    my $self = shift->new({ 'input' => shift || {} });

    my $oldExperimentID =  $self->input->{'siteExperimentID'};

    if (not $oldExperimentID) {
        $self->data->{'dataType'} = "json";
        $self->data->{'data'}->{'error'} = "siteExperimentID required";
        return $self->result;
    }

    my $schema = ECS::DB::Schema::getSchema({
        'dbh' => $self->dbh,
        'schema' => "SiteExperiment",
    });

    my $rs = $schema->search(
        {
            'siteExperimentID' => $oldExperimentID,
        },
    );
    my $oldExperiment = $rs->next;

    if (not $oldExperiment) {
        $self->data->{'dataType'} = "json";
        $self->data->{'data'}->{'error'} =
            "siteExperimentID $oldExperimentID does not exist";
        return $self->result;
    }

    # Put all of this in a single transaction so that no website request will
    #  occur while the siteExperimentID is being recreated
    $self->dbh->begin_work;

    $oldExperiment->delete;
    my $newExperiment = $oldExperiment->copy;
    $newExperiment->insert;
    my $newExperimentID = $newExperiment->siteExperimentID;

    my $sthUpdateCases = $self->dbh->prepare(
        'UPDATE siteExperimentCase
        SET siteExperimentID = ?
        WHERE siteExperimentID = ?'
    );

    $sthUpdateCases->execute(
        $newExperimentID,
        $oldExperimentID,
    );

    $self->dbh->commit;

    $self->data->{'dataType'} = "json";
    $self->data->{'data'}->{'success'} = 1;
    return $self->result;
}

###############################################################################


1;
