#!/usr/bin/perl

use strict;
use warnings;

use utf8;

use open ":std" => "utf8";

use Text::Diff;

use Yandex::DBTools;
use Yandex::DBShards;
use Yandex::ListUtils qw/xisect/;
use Yandex::Shell;
use ProjectSpecific qw/get_db_conf_file/;

my $direct_ro_user = 'direct-ro';
my $tables_to_skip_re = '^(?:.*_tmp_[0-9]+)$';
my $column_definitions_re = '(^\s+\w.+)\n^\)';

run() unless caller();

sub run {
    die "Usage: $0 <db1> <db2> [<table1>] [<table2>] ...\nExamples:\n$0 ts:ppc:1 tsb:ppc:1\n$0 ts:ppc:1 tsb:ppc:1 campaigns\n" if @ARGV < 2;

    my ($db1, $db2, @tables_to_compare) = @ARGV;

    my $no_differences = 1;
    if (! @tables_to_compare) {
        my %tables_in;
        my %str_list_of_tables_in;
        for my $db ($db1, $db2) {
            my ($conf) = parse_db_def($db);
            my @show_tables_cmd = (
                'dbs-sql',
                # в продакшеновые БД ходим ридонли пользователем
                is_prod_conf($conf) ? ('-u', $direct_ro_user) : (),
                $db,
                'show tables',
                '-B'
            );
            my @tables = grep { ! /$tables_to_skip_re/i } split /\n/, yash_qx(@show_tables_cmd);
            shift @tables;  # "Tables_in_<db>"
            $tables_in{ $db } = \@tables;
            $str_list_of_tables_in{ $db } = join "\n", sort(@tables), '';
        }

        my $diff = diff(\($str_list_of_tables_in{ $db1 }), \($str_list_of_tables_in{ $db2 }), { FILENAME_A => $db1, FILENAME_B => $db2 });
        if ($diff) {
            $no_differences = 0;
            print "# all tables\n";
            print $diff;
            print "\n";
        }

        @tables_to_compare = @{ xisect($tables_in{ $db1 }, $tables_in{ $db2 }) };
    }

    my %create_table;
    for my $db ($db1, $db2) {
        $create_table{$db} = get_create_table_statements_from_db($db);
    }

    for my $table (@tables_to_compare) {

        my %to_diff;
        for my $db ($db1, $db2) {
            my $create_table_sql = $create_table{ $db }->{ $table } || '';
            $create_table_sql =~ s/auto_increment=[0-9]+\s?//i;
            $create_table_sql =~ s/CONSTRAINT `[^`]+`/CONSTRAINT ``/ig;

            $create_table_sql =~ m!$column_definitions_re!ims;
            my $column_definitions_new = join("\n", sort split /\n/, "$1,");
            $create_table_sql =~ s/$column_definitions_re/$column_definitions_new\n\)/ims;

            $to_diff{ $db } = $create_table_sql;
        }

        my $diff = diff(\$to_diff{ $db1 }, \$to_diff{ $db2 }, { FILENAME_A => $db1, FILENAME_B => $db2 });
        if ($diff) {
            $no_differences = 0;
            print "# $table\n";
            print $diff;
            print "\n\n";
        }
    }

    if ($no_differences) {
        print "no differences\n";
        exit 0;
    } else {
        exit 2;
    };
}

# "sb:ppc:all" -> ("sb", "ppc")
sub parse_db_def {
    my ($db_def) = @_;

    # эвристика: конфигурация -- первая часть из db_def
    $db_def =~ /^(.*?):(.*)$/ or die "can't parse db '$db_def'";
    my ($conf, $db) = ($1, $2);

    return ($conf, $db);
}

sub is_prod_conf {
    my ($conf_name) = @_;

    return $conf_name =~ /^(pr|prod|production|sb|sandbox)$/;
}

# mysqldump вместо единичных show create table -- быстрее
sub get_create_table_statements_from_db
{
    my ($db) = @_;
    (my $conf_name, $db) = parse_db_def($db);
    $Yandex::DBTools::CONFIG_FILE = get_db_conf_file($conf_name);
    my $cfg = get_db_config($db, (is_prod_conf($conf_name) ? (db_user => $direct_ro_user) : ()));
    my $host = ref $cfg->{host} ? $cfg->{host}->[0] : $cfg->{host};
    my $user = $cfg->{user};
    my $pass = $cfg->{pass};
    my @mysqldump_args = (
        '-f',
        '--set-gtid-purged=OFF',
        '--default-character-set=utf8',
#        '--skip-lock-tables',
        '--single-transaction',
        '--no-data',
        '--compact',
        "--user=$user",
        "--host=$host",
        "--port=$cfg->{port}",
        $cfg->{db},
    );
    $ENV{MYSQL_PWD} = $cfg->{pass};
    my $dump = yash_qx("mysqldump", @mysqldump_args);

    my $res = {};
    my @table_lines;
    my $table_name = "";
    for my $line ( split "\n", $dump ){
        next if $line =~ m!^\s*/\*.*\*/\s*!;
        if ( $line =~ /CREATE TABLE `(.*?)`/ ){
            my $table_name_next = $1;
            $res->{$table_name} = join("\n", @table_lines) if $table_name ne "";
            @table_lines = ();
            $table_name = $table_name_next;
        }
        push @table_lines, $line;
    }
    $res->{$table_name} = join "\n", @table_lines;

    return $res;
}
