#!/usr/bin/perl

use strict;
use warnings;
use open ':std', ':encoding(UTF-8)';

use Parallel::ForkManager;
use Term::ANSIColor qw(:constants);
use List::MoreUtils qw(first_index);
use Digest::MD5 qw(md5 md5_hex md5_base64);

use lib '/app/tools/common/lib';
use Common::Session;
use Common::DB::Diff::Table;

my ( $opt, $usage ) = clopt(
    [ 'help|?|h'     => 'print usage message and exit' ],
    [ 'database|d=s' => 'Database to fix' ],
    [ 'primary|p=s'  => 'Primary or source database : db01',      { 'default' => 'db01' } ],
    [ 'replica|r=s'  => 'Replica or destination database : db02', { 'default' => 'db02' } ],
    [ 'table|t=s'    => 'Table to fix' ],
);

if ( defined $opt->table and not defined $opt->database ) {
    logMessag( 'fatal', "Option table requires option database" );
    exit 1;
}

my $primary = $opt->primary;
my $replica = $opt->replica;

my $p_dbh = Common::Session::getdbh($primary);
my $r_dbh = Common::Session::getdbh($replica);

my $dbx = Common::Session::getdbx('admindb');

## get all database diffs
my $search = { 'is_diff' => 'Y' };
if ( defined $opt->database ) {
    $search->{'dbname'} = $opt->database;
    if ( defined $opt->table ) {
        $search->{'tabname'} = $opt->table;
    }
}

my $rset = $dbx->resultset('Report::SysDbReplicaDiff')->search($search);
while ( my $row = $rset->next ) {

    next if not defined $row->diff_json;

    my $db  = $row->dbname;
    my $tab = $row->tabname;
    my $key = $row->keycol;

    my $loop  = 1;
    my $fixed = 0;
    until ( $fixed or $loop >= 3 ) {

        logMessage( 'info', "checking difference for $db.$tab.$key loop: $loop" );

        my $tdiff = Common::DB::Diff::Table->new( $db, $tab, $primary, $replica );
        if ( defined $tdiff->primary->_pk_exception($tab) ) {
            logMessage( 'info1', "$tab has no primary key - update by export/import of table" );
            $loop = 4;
			next;
        }

        my $diff;
        if ( $loop > 1 ) {
            my ( $err, $msg, $minrow, $maxrow, $pcnt, $rcnt ) = $tdiff->compare_signatures( $row->minrow, $row->maxrow, $p_dbh, $r_dbh );
            if ($err) {
                logMessage( 'info1', "row block has different signature - check for differences" );
                if ( $pcnt == $rcnt ) {
                    $diff = $tdiff->compare_columns( $minrow, $maxrow, $p_dbh, $r_dbh );
                    if ( defined $diff ) {
                        logMessage( 'info1', "differences found - updating replica: $replica" );
                        update_diff( $r_dbh, $diff, $db, $tab, $key );
                    }
                } else {
                    logMessage( 'info1', "Row count mismatch new to replicate $db.$tab to replica: $replica" );
                    $loop = 4;
                }
            } else {
                $fixed = 1;
            }
        } else {
            if ( defined $row->diff_json and length( $row->diff_json ) < 65535 ) {
                $diff = JSON::XS::decode_json( $row->diff_json );
                update_diff( $r_dbh, $diff, $db, $tab, $key );
            }
        }
        $loop++;
    }
    if ($fixed) {
        logMessage( 'info1', "removing diff row from admin db" );
        $row->delete;
    }
}

sub update_diff {
    my $r_dbh = shift;
    my $diff  = shift;
    my $db    = shift;
    my $tab   = shift;
    my $key   = shift;
    foreach my $pk ( sort keys %{$diff} ) {
        my $cols;
        my $vals;
        map { push @{$cols}, "$_=?"; push @{$vals}, $diff->{$pk}->{$_}->{'primary'} } keys %{ $diff->{$pk} };
        $vals = [ map { $_ eq 'NULL' ? undef : $_ } @{$vals} ];
        my $sql = "UPDATE $db.$tab SET ";
        $sql .= join ',', @{$cols};
        $sql .= " where $key = $pk;";
        logMessage( 'info2', $sql );
        my $r_sth = $r_dbh->prepare($sql);
        $r_sth->execute( @{$vals} );

        if ( $r_sth->rows ) {
            logMessage( 'info2', "$db.$tab.$key updated" );
        } else {
            logMessage( 'error2', "$db.$tab.$key something went wrong" );
        }
    }
}
