#!/usr/bin/perl
use strict;


use Data::Dumper;
use Getopt::Std;
use POSIX qw(ceil);
use POSIX ":sys_wait_h";
use Sys::Hostname;
use Carp;


use lib '/app/tools/common/lib';
use Common::DB::Item;
use Common::Assert;
use Common::TextProgressBar;
use Common::Timer;
use Common::RSDB;
use Common::Util;
use Common::DB::Item::Service;


use lib '/app/tools/rps/lib';
use RPS::DB::Item::MechanicalCarryover;
use RPS::DB::Item::TrackLicense;
use RPS::DB::Item::Track;
use RPS::DB::Item::TrackContract;
use RPS::DB::Item::Master;
use RPS::DB::Item::Album;
#use RPS::DB::Item::License;
use RPS::DB::Item::LicenseReserve;
use RPS::DB::Item::LicenseReserveLiquidation;
use RPS::DB::Item::StatRate;
use RPS::DB::Item::ProductTrack;
use RPS::DB::Item::Song;

use RPS::DB::Item::Publisher;
use RPS::DB::Item::Product;
use RPS::DB::Item::ControlledComposition;

use RPS::DB::Item::AlbumContract;
use RPS::DB::Item::MechanicalRun;
use RPS::DB::Item::MechanicalStatement;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::MechanicalStatementTrack;
use RPS::DB::Item::MechanicalStatementLicense;
use RPS::DB::Item::MechanicalStatementAdjustmentItem;
use RPS::DB::Item::SaleMechanicalStatementItemMap;

use RPS::License::TrackLicense;
use RPS::License::TrackLicenseList;
use RPS::DB::Item::NewArtistContract;
use RPS::DB::Item::ReserveLiquidation;



my $gVerbosityLevel = 1;

# Parse the command-line options.
#
my %options;
_parseCommandLine(\%options);

my $clientID = $options{clientID};

_transferRaptorRSCOMMON();


# need to merge the user table first, and build the raptor->rps id mapping table.
# Then have to _merge_ the phone, address, user_access and user_logins tables

my ($userIDMap, $newUsers) = _mergeUser();

_mergeAddress($userIDMap, $newUsers);

_alterAddress($clientID);

_alterUser($userIDMap, $newUsers);

_renameRSCOMMON_RAPTOR();

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

sub _renameRSCOMMON_RAPTOR
{
    `mysqldump -uroot RSCOMMON_RAPTOR > /tmp/RSCOMMON_RAPTOR`;
    `mysql -uroot -e 'drop database RSCOMMON_RAPTOR'`;
    `mysql -uroot RSCOMMON < /tmp/RSCOMMON_RAPTOR`;
}


sub _alterUser
{
    my ($userIDMap, $newUsers) = @_;

 	my $dbo_raptor = new Common::RSDB(client_id => 999, db_name => 'RSCOMMON_RAPTOR');
 	my $dbo_rps = new Common::RSDB(client_id => 999, db_name => 'RSCOMMON');


    # Make a new user id map, which we'll use later when iterating through the table.
    #
    my %newUserMap;
    foreach my $newUserData (@$newUsers)
    {
        my $rpsUserID = $newUserData->{user_id};
        my $newID = $userIDMap->{$rpsUserID};
        $newUserMap{$newID} = 1;
    }


    # Ditch the phone_id column, replace with phone1,phone2,phone3
    #
	my $sql = "ALTER TABLE user ADD COLUMN phone1 varchar(32) default NULL";
    if (!$dbo_raptor->DoCmd($sql))
    {
        _report("error " . $dbo_raptor->LastErrorStr());
        return;
    }

	$sql = "ALTER TABLE user ADD COLUMN phone2 varchar(32) default NULL";
    if (!$dbo_raptor->DoCmd($sql))
    {
        _report("error " . $dbo_raptor->LastErrorStr());
        return;
    }

	$sql = "ALTER TABLE user ADD COLUMN phone3 varchar(32) default NULL";
    if (!$dbo_raptor->DoCmd($sql))
    {
        _report("error " . $dbo_raptor->LastErrorStr());
        return;
    }


    # Now iterate through the user table and populate phone1.
    # (I happen to know there these is no more than 1 phone per user... so I can cheat a bit)
    #
    my $sth = $dbo_raptor->DoCmd("SELECT * from user");
    while (my $hr = $sth->fetchrow_hashref())
    {
        if ($hr->{phone_id})
        {
            if ($newUserMap{$hr->{user_id}})
            {
                # Get the phone record from RPS
                #
                my $pSTH = $dbo_rps->DoCmd("SELECT phone_number from phone where phone_id=".$hr->{phone_id});
                my $pHR = $pSTH->fetchrow_hashref();
                if ($pHR)
                {
                    my $sql = "UPDATE user SET phone1=".$dbo_raptor->DBQuote($pHR->{phone_number})
                     . " WHERE user_id=".$hr->{user_id};
                    $dbo_raptor->DoCmd($sql);
                }
            }
            else
            {
                my $pSTH = $dbo_raptor->DoCmd("SELECT phone_number from phone where phone_id=".$hr->{phone_id});
                my $pHR = $pSTH->fetchrow_hashref();
                if ($pHR)
                {
                    my $sql = "UPDATE user SET phone1=".$dbo_raptor->DBQuote($pHR->{phone_number})
                     . " WHERE user_id=".$hr->{user_id};
                    $dbo_raptor->DoCmd($sql);
                }
            }
        }
    }
}


sub _mergeAddress
{
    my ($userIDMap, $newUsers) = @_;

 	my $dbo_raptor = new Common::RSDB(client_id => 999, db_name => 'RSCOMMON_RAPTOR');
 	my $dbo_rps = new Common::RSDB(client_id => 999, db_name => 'RSCOMMON');

    
    # Here's the trick - unlike user, we don't have a unique field we can use to determine whether
    # this address is 'new' or not.
    #
    # I don't really _care_ about users that have _moved_. I only care about _new_ users... I will need
    # to bring their addresses over.  The rest, honestly, are irrelevant.
    #
    # Going to also need to update the address_id field for these new users to reflect the new address ids.
    #

    # Bring over addresses from RPS that belong to new users (re-mapping the user_id field along the way).
    #
    foreach my $rpsUser (@$newUsers)
    {
        my $rpsUserID = $rpsUser->{user_id};
        my $newID = $userIDMap->{$rpsUserID};

        my $sth = $dbo_rps->DoCmd("SELECT * from address where user_id=$rpsUserID");
        my $newPrimaryID;
        while (my $hr = $sth->fetchrow_hashref())
        {
            my $oldAddressID = $hr->{address_id};
            my $sql = "INSERT INTO address SET";
            $sql .= " user_id=$newID";
            $sql .= ",address_type_id=".$hr->{address_type_id};
            $sql .= ",address1=".$dbo_raptor->DBQuote($hr->{address1}) if ($hr->{address1});
            $sql .= ",address2=".$dbo_raptor->DBQuote($hr->{address2}) if ($hr->{address2});
            $sql .= ",city=".$dbo_raptor->DBQuote($hr->{city}) if ($hr->{city});
            $sql .= ",state=".$dbo_raptor->DBQuote($hr->{state}) if ($hr->{state});
            $sql .= ",province=".$dbo_raptor->DBQuote($hr->{province}) if ($hr->{province});
            $sql .= ",postal_code=".$dbo_raptor->DBQuote($hr->{postal_code}) if ($hr->{postal_code});
            $sql .= ",country=".$dbo_raptor->DBQuote($hr->{country}) if ($hr->{country});
            $sql .= ",date_created=".$dbo_raptor->DBQuote($hr->{date_created}) if ($hr->{date_created});
            $sql .= ",date_modified=".$dbo_raptor->DBQuote($hr->{date_modified}) if ($hr->{date_modified});

            $dbo_raptor->DoCmd($sql);
            my $newAddressID = $dbo_raptor->LastInsertID();

            # Was this address the user's primary?
            #
            if ($rpsUser->{address_id} == $oldAddressID)
            {
                $newPrimaryID = $newAddressID;
            }
        }

        if ($newPrimaryID)
        {
            $dbo_rps->DoCmd("UPDATE user set address_id=$newPrimaryID where user_id=$newID");
        }
    }
}


sub _transferRaptorRSCOMMON
{

    my $dbName = 'RSCOMMON';
    _report("dbName : $dbName", 4);
    

    # So, first we'll need to tell the remote host to dump the raptor database.
    #
    _report("-- extracting database $dbName on raptor01");
    my $cmd = "rm -f /tmp/$dbName*;mysqldump -uroot $dbName > /tmp/$dbName;gzip /tmp/$dbName";
    `ssh raptor01 '$cmd'`;


    # Now we copy it over to here.
    #
    _report("-- copying database dump to local host");
    `rm -f /tmp/$dbName*`;
    `scp -o 'TCPKeepAlive yes' raptor01:/tmp/$dbName.gz /tmp/$dbName.gz`;


    # Expand it.
    #
    _report("-- uncompressing data");
    `gunzip /tmp/$dbName.gz`;


    # Create the new raptor db on the local host.
    #
    my $newDBName = $dbName . "_RAPTOR";
    _report("-- importing data into new database $newDBName");
    `mysql -uroot -e 'create database $newDBName'`;
    `mysql -uroot $newDBName < /tmp/$dbName`;
}


sub _mergeUser
{
 	my $dbo_raptor = new Common::RSDB(client_id => 999, db_name => 'RSCOMMON_RAPTOR');
 	my $dbo_rps = new Common::RSDB(client_id => 999, db_name => 'RSCOMMON');

    my %map;
    my @newUsers;

    # Iterate through the RPS version of user.
    # We're looking to find both new users, and
    # users that have different ids.
    #
    my $searchSQL = "SELECT * from user";
    my $searchSTH = $dbo_rps->DoCmd($searchSQL);
    while (my $hr = $searchSTH->fetchrow_hashref())
    {
        my $email = $hr->{email};
        my $raptorSTH = $dbo_raptor->DoCmd("SELECT user_id from user where email=" . $dbo_raptor->DBQuote($email));
        my $raptorHR = $raptorSTH->fetchrow_hashref();
        if (! $raptorHR || ! $raptorHR->{user_id})
        {
            #new user = insert at the end (later)
            #
            push @newUsers, $hr;
        }
        elsif ($raptorHR->{user_id} != $hr->{user_id})
        {
            # same user, different id
            #
            $map{$hr->{user_id}} = $raptorHR->{user_id};
            _report("user " . $hr->{user_id} . " exists in Raptor as id " . $raptorHR->{user_id}, 4);
        }
    }

    # Create entries for all the new users
    # Going to have to re-map primary_contact_ids for these that are being moved.
    # I believe I can limit this to just these that have been moved.
    #
    my @needToAdjustContactIDs;
    _report("have to create " . scalar @newUsers . " users", 4);
    foreach my $data (@newUsers)
    {
        # Raptor is reasonably full, so don't worry about trying to re-use the id.
        # Just take this new user onto the back of the line.
        #
        my $sql = "INSERT INTO user SET";
        $sql .= " email=" . $dbo_rps->DBQuote($data->{email});
        $sql .= ",password=" . $dbo_rps->DBQuote($data->{password}) if (defined $data->{password});
        $sql .= ",first_name=" . $dbo_rps->DBQuote($data->{first_name});
        $sql .= ",last_name=" . $dbo_rps->DBQuote($data->{last_name});
        $sql .= ",address_id=" . $data->{address_id} if (defined $data->{address_id});
        $sql .= ",phone_id=" . $data->{phone_id} if (defined $data->{phone_id});
        $sql .= ",primary_contact_id=" . $data->{primary_contact_id} if (defined $data->{primary_contact_id});
        $sql .= ",secondary_contact_id=" . $data->{secondary_contact_id} if (defined $data->{secondary_contact_id});
        $sql .= ",disabled=" . $data->{disabled};
        $sql .= ",date_created=" . $dbo_rps->DBQuote($data->{date_created}) if (defined $data->{date_created});
        $sql .= ",date_modified=" . $dbo_rps->DBQuote($data->{date_modified}) if (defined $data->{date_modified});

        $dbo_raptor->DoCmd($sql);
        my $newID = $dbo_raptor->LastInsertID();
        _report(" moved " . $data->{user_id} . " to $newID", 4);
        $map{$data->{user_id}} = $newID;
        push @needToAdjustContactIDs, $newID;
    }

    _report("map: - " . Dumper(\%map), 4);

    # Now adjust the contact ids for these that have moved.
    #
    foreach my $id (@needToAdjustContactIDs)
    {
        my $sql = "select primary_contact_id, secondary_contact_id from user where user_id=$id";
        my $sth = $dbo_raptor->DoCmd($sql);
        my $hr = $sth->fetchrow_hashref();
        my $newPrimary;
        my $newSecondary;
        if ($hr->{primary_contact_id} > 0)
        {
            # assuming if they have 1, they have both
            #
            $newPrimary = $map{$hr->{primary_contact_id}};
            $newPrimary = $hr->{primary_contact_id} unless $newPrimary;
            $newSecondary = $map{$hr->{secondary_contact_id}};
            $newSecondary = $hr->{secondary_contact_id} unless $newSecondary;
            $dbo_raptor->DoCmd("UPDATE user set primary_contact_id=$newPrimary, secondary_contact_id=$newSecondary where user_id=$id");
        }
    }

    return (\%map, \@newUsers);
}



sub _updateHRToDB
{
    my ($hr, $dbo, $tableName, $keyColumn, $keyValue) = @_;

    my (@keys, @values);

    foreach my $key (keys %$hr)
    {
        next if ($key eq $keyColumn);
        push @values, $hr->{$key};

        push @keys, "$key=?";
    }
    my $setClause = join(',', @keys);

    my $sql = "INSERT INTO $tableName SET $setClause WHERE $keyColumn = $keyValue";
    $dbo->DoCmdWithPlaceholders($sql, \@values);
}


sub _alterAddress
{
    my ($clientID) = @_;

    _report("altering address table");


    # Replacing the two seperate state and province columns with a single state_province table.
    #
 	my $dbo = new Common::RSDB(client_id => 999, db_name => 'RSCOMMON_RAPTOR');

	my $sql = "ALTER TABLE address ADD COLUMN state_province varchar(80) default NULL";
    if (!$dbo->DoCmd($sql))
    {
        _report("error " . $dbo->LastErrorStr());
        return;
    }

	$sql = "ALTER TABLE address ADD COLUMN street_address varchar(80) default NULL";
    if (!$dbo->DoCmd($sql))
    {
        _report("error " . $dbo->LastErrorStr());
        return;
    }

    # populate the state_province 
    #
    $sql = "SELECT address_id,state,province from address";
    my $sth = $dbo->DoCmd($sql);
    while (my $hr = $sth->fetchrow_hashref())
    {
        my $sp = $hr->{state};
        if (! $sp)
        {
            $sp = $hr->{province};
        }

        if ($sp)
        {
            $dbo->DoCmd("UPDATE address set state_province =" . $dbo->DBQuote($sp) . " WHERE address_id=" . $hr->{address_id});
        }
    }

    # populate the street_address column by concatenating address1 and address2
    #
    $sql = "SELECT address_id,address1,address2 from address";
    $sth = $dbo->DoCmd($sql);
    while (my $hr = $sth->fetchrow_hashref())
    {
        my $newAddress = $hr->{address1};
        if ($newAddress && $hr->{address2})
        {
            $newAddress .= ', ' . $hr->{address2};
        }

        $dbo->DoCmd("UPDATE address set street_address=" . $dbo->DBQuote($newAddress) . " WHERE address_id=" . $hr->{address_id});
    }

    
    # Drop the old columns
    #
    $sql = "ALTER TABLE address DROP COLUMN address1";
    if (!$dbo->DoCmd($sql))
    {
        _report("error " . $dbo->LastErrorStr());
        return;
    }

    $sql = "ALTER TABLE address DROP COLUMN address2";
    if (!$dbo->DoCmd($sql))
    {
        _report("error " . $dbo->LastErrorStr());
        return;
    }
    $sql = "ALTER TABLE address DROP COLUMN state";
    if (!$dbo->DoCmd($sql))
    {
        _report("error " . $dbo->LastErrorStr());
        return;
    }
    $sql = "ALTER TABLE address DROP COLUMN province";
    if (!$dbo->DoCmd($sql))
    {
        _report("error " . $dbo->LastErrorStr());
        return;
    }
}


sub _usage
{
    print "\nusage: $0 -c client_id [-V]\n";
    print "\n";
    print "Arguments:\n";
    print "\t-c <client_id>\t\tThe client_id of the client to process\n";
    print "\t-V <N>\t\t\tVerbosity level - 0 means no output\n";
}


sub _report
{
    my ($string, $verbosity) = @_;
    $verbosity = 1 unless defined $verbosity;

    if ($gVerbosityLevel >= $verbosity)
    {
        print STDERR $string . "\n";
    }
}


# Pass dates in 'yyyy-mm-dd' format.
#
sub _parseCommandLine
{
    my ($settings) = @_;

    my %opt;
    getopts('dc:V:', \%opt);

    if ($opt{d})
    {
        $settings->{deleteFirst} = 1;
    }


    if (! $opt{c})
    {
        _usage();
        exit(1);
    }
    $settings->{clientID} = $opt{c};

    if (defined $opt{V})
    {
        $gVerbosityLevel = $opt{V};
    }
}

