package BookPub::RDAS::Request::POSData::Apple;

use WWW::Mechanize;
use Data::Dumper;

use lib '/app/tools/common/lib';
use Common::Date;
use Common::Assert;

use lib '/app/tools/rdas/lib';
use RDAS::Fetcher::Mechanize::Authenticated;

use lib '/app/tools/bookpub/lib';
use BookPub::ServiceCredential;

use BookPub::RDAS::Form::POSData::File::Apple;
use BookPub::RDAS::Response::POSData::Apple;
use BookPub::RDAS::URL::ProductPrice::Apple;
use BookPub::RDAS::Parser::ProductPrice::Apple::Home;
use BookPub::RDAS::Parser::POSData::Apple::VendorPage;
use BookPub::RDAS::Parser::POSData::Apple::ReportDates;

use base 'BookPub::RDAS::Request::POSData';

use constant kLoginURL      => 'https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa';
use constant kReportBaseURL => 'https://reportingitc.apple.com';
use constant kVendorURL     => 'https://reportingitc.apple.com/vendor_default.faces';
use constant kReportURL     => 'https://reportingitc.apple.com/sales.faces';

sub _init {
    my $self = shift;

    $self->{_mechanize} = new WWW::Mechanize();
    $self->mechanize->agent('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5');
    $self->mechanize->cookie_jar( {} );

    my $date = Common::Date->now();
    $self->{_yesterday} = $date->clone->addDays(-1);
    $self->{_sunday}    = $date->clone->addDays( $date->asString("%w") * -1 );

    return $self->SUPER::_init(@_);
}

sub scriptID    { shift->{_scriptID} }
sub mechanize   { shift->{_mechanize} }
sub yesterday   { shift->{_yesterday} }
sub sunday      { shift->{_sunday} }
sub vendorID    { shift->{_vendorID} }
sub ajaxID      { shift->{_ajaxID} }
sub viewState   { shift->{_viewState} }
sub reportDates { shift->{_reportDates} }
sub reports     { shift->{_reports} }

sub dateFormat { '%m/%d/%Y' }

sub _getResponseObject {
    my $self = shift;
    return new BookPub::RDAS::Response::POSData::Apple( reports => $self->reports, reportDate => $self->reportDate, @_ );
}

sub _getFileFormObject {
    my $self = shift;
    return new BookPub::RDAS::Form::POSData::File::Apple(@_);
}

sub fetch {

    # Disable warnings coming from HTTP::Cookie and HTTP::Message.
    $^W = 0;

    my $self = shift;
    $self->{_error} = undef;

    # Login
    unless ( $self->_login() ) {
        Log->error("Authentication failed");
        return;
    }

    # Vendor Page
    unless ( $self->_vendorPage() ) {
        Log->error("Vendor page fetch failed");
        return;
    }

    # Main sales report page
    unless ( $self->_saleReportDates() ) {
        Log->error("Sale report page fetch failed");
        return;
    }

    $self->_initPOSFiles();

    # Now that all the initialization work is done lets actually do some real work
    $self->_storeMissingReports();

    $self->_POSFileMissing();

    return 1;
}

sub _credentials {
    my $self = shift;

    unless ( $self->{_credentials} ) {
        my $serviceID = $self->_getResponseObject()->serviceID();
        my $cred = new BookPub::ServiceCredential( serviceID => $serviceID );
        if ( $cred && $cred->Username && $cred->Password ) {
            $self->{_credentials} = [ $cred->Username, $cred->Password ];
        }
    }

    if ( $self->{_credentials} ) {
        return @{ $self->{_credentials} };
    } else {
        die "No login credentials";
    }
}

sub _postData {
    my $self = shift;
    my $mech = $self->mechanize();
    my $url  = shift || die;
    my $args = shift;

    Log->info("  Posting data to URL: $url");
    Log->debug( "    Post Data: " . Dumper $args );

    return $mech->post( $url, $args );
}

sub _login {
    my $self = shift;
    my ( $uname, $passwd ) = $self->_credentials();

    Log->notice(" + Logging into iTunes Connect");
    Log->info( "   - URL: " . kLoginURL );

    $self->mechanize->get(kLoginURL);
    $self->mechanize->submit_form(
        form_name => 'appleConnectForm',
        fields    => {
            theAccountName => $uname,
            theAccountPW   => $passwd
        },
    );

    my $parser = new BookPub::RDAS::Parser::ProductPrice::Apple::Home( content => $self->mechanize->content() );

    if ( $parser->Login ) {
        return;
    }

    return 1;
}

# With the vendor page we have to first grab the page, then post back to the page.
sub _vendorPage {
    my $self = shift;

    Log->notice(" + Accessing Main Report Page");
    Log->info( "   - URL: " . kReportBaseURL );

    $self->mechanize->get(kReportBaseURL);

    return unless ( $self->mechanize->content );
    my $parser = new BookPub::RDAS::Parser::POSData::Apple::VendorPage( content => $self->mechanize->content() );

    $self->{_vendorID}  = $parser->VendorID();
    $self->{_ajaxID}    = $parser->AJAXID();
    $self->{_viewState} = $parser->ViewState();

    unless ( $self->vendorID ) {
        Log->error(" unable to find vendor id");
        return;
    }

    unless ( $self->ajaxID ) {
        Log->error(" unable to find ajax id");
        return;
    }

    unless ( $self->viewState ) {
        Log->error(" unable to find view state");
        return;
    }

    Log->info( "  - Set Vendor ID " . $self->vendorID() );
    Log->info( "  - Set Ajax ID " . $self->ajaxID() );
    Log->info( "  - Set View State " . $self->viewState() );

    Log->notice(" + Accessing Main Vendor Page");
    Log->info( "   - URL: " . kVendorURL );

    my $vendorID = "defaultVendorPage:" . $self->vendorID;
    $self->_postData(
        kVendorURL,
        {
            AJAXREQUEST             => $self->ajaxID,
            defaultVendorPage       => 'defaultVendorPage',
            $vendorID               => $vendorID,
            $self->vendorID         => $self->vendorID,
            'javax.faces.ViewState' => $self->viewState,
        }
    );

    return 1;
}

sub _saleReportDates {
    my $self = shift;

    Log->notice(" + Collecting Sale Report Dates");
    Log->info( "   - URL: " . kReportURL );

    $self->mechanize->get(kReportURL);

    my $parser = new BookPub::RDAS::Parser::POSData::Apple::ReportDates( content => $self->mechanize->content() );

    $self->{_viewState} = $parser->ViewState();

    $self->{_scriptID} = $parser->ScriptID();

    if ( $self->scriptID ) {
        Log->info( "  Setting script id to " . $self->scriptID );
    } else {
        Log->error(" ** Fail to set script id ** ");
        return;
    }

    my $dates = $parser->Dates();

    if ($dates) {
        Log->info("Found report dates: $dates");
    } else {
        Log->error("could not find report dates");
        return;
    }

    my @dates = split( /,/, $dates );
    $self->{_reportDates} = \@dates;

    return 1;
}

# This method will either attempt to download all available sales reports that
# we haven't seen or the report specified on the command line.
sub _storeMissingReports {
    my $self = shift;

    Log->notice(" + Starting report download process");

    my @dates = $self->_getReportDates();

    if (@dates) {
        Log->info(" - Report dates we are after: @dates");
    } else {
        Log->info("No new reports to download");
        return;
    }

    return unless ( $self->_initSalesDownload() );

    foreach my $date (@dates) {
        return unless ( $self->_downloadSalesReport($date) );
    }

    return 1;
}

sub _getReportDates {
    my $self = shift;
    my @dates;

    my $reportDate = $self->reportDate();

    # If we have a user specified report date
    if ($reportDate) {
        foreach my $d ( @{ $self->reportDates } ) {
            return $d if ( Common::Date->new($reportDate)->asString() eq Common::Date->new($d)->asString() );
        }

        Log->error("Report date $reportDate not available");
        return;

        # Otherwise let's build the list from the date reported by apple.
    } else {
        my @reportDates = $self->reportDates ? @{ $self->reportDates } : ();
        foreach my $d (@reportDates) {
            unless ( $self->feed->isPassedStartDate($d) ) {
                Log->info("  -- Ignoring date $d, not passed start date");
                next;
            }

            unless ( $self->feed->isNew($d) ) {
                Log->info("  -- Ignoring date $d, is older than last download");
                next;
            }

            Log->info("  -- Adding date $d");
            push( @dates, $d );
        }

        return @dates;
    }
}

sub _initSalesDownload {
    my $self = shift;

    Log->notice("Setup daily report download on itunes");

    my $dateName = $self->scriptID();
    $dateName =~ s/_6/_8/;

    my $dailyName = $self->scriptID();

    Log->info("  -- Set the initial date on connect");
    $self->_postData(
        kReportURL,
        {
            AJAXREQUEST                                  => $self->ajaxID,
            theForm                                      => 'theForm',
            'theForm:xyz'                                => 'normal',
            'theForm:vendorType'                         => 'N',
            'theForm:datePickerSourceSelectElementSales' => $self->yesterday->asString('%m/%d/%Y'),
            'theForm:weekPickerSourceSelectElement'      => $self->sunday->asString('%m/%d/%Y'),
            'javax.faces.ViewState'                      => $self->viewState,
            $dateName                                    => $dateName,
        }
    );

    my $parser = new BookPub::RDAS::Parser::POSData::Apple( content => $self->mechanize->content() );
    $self->{_viewState} = $parser->ViewState();

    unless ( $self->viewState ) {
        Log->error("  ** Failed to download sales report for date $reportDate");
        return;
    }

    Log->info("  -- Tell connect we want daily reports");
    $self->_postData(
        kReportURL,
        {
            AJAXREQUEST                                  => $self->ajaxID,
            theForm                                      => 'theForm',
            'theForm:xyz'                                => 'normal',
            'theForm:vendorType'                         => 'N',
            'theForm:optInVar'                           => 'A',
            'theForm:optInVarRender'                     => 'false',
            'theForm:wklyBool'                           => 'false',
            'theForm:datePickerSourceSelectElementSales' => $self->yesterday->asString('%m/%d/%Y'),
            'theForm:weekPickerSourceSelectElement'      => $self->sunday->asString('%m/%d/%Y'),
            'javax.faces.ViewState'                      => $self->viewState,
            $dailyName                                   => $dailyName,
        }
    );

    $parser = new BookPub::RDAS::Parser::POSData::Apple( content => $self->mechanize->content() );
    $self->{_viewState} = $parser->ViewState();

    unless ( $self->viewState ) {
        Log->error("  ** Failed to download sales report for date $reportDate");
        return;
    }

    return 1;
}

sub _downloadSalesReport {
    my $self = shift;
    my $reportDate = shift || return;

    Log->notice("Starting sales report download process for $reportDate");

    return $self->_fetchSalesReport($reportDate);
}

sub _fetchSalesReport {
    my $self = shift;
    my $reportDate = shift || return;

    Log->notice("  -- Fetching report for $reportDate");

    my $selectName = $self->scriptID();
    $selectName =~ s/_6/_43/;

    $self->_postData(
        kReportURL,
        {
            AJAXREQUEST                                  => $self->ajaxID,
            theForm                                      => 'theForm',
            'theForm:xyz'                                => 'normal',
            'theForm:vendorType'                         => 'N',
            'theForm:optInVar'                           => 'A',
            'theForm:optInVarRender'                     => 'false',
            'theForm:wklyBool'                           => 'false',
            'theForm:datePickerSourceSelectElementSales' => $reportDate,
            'theForm:weekPickerSourceSelectElement'      => $self->sunday->asString('%m/%d/%Y'),
            'javax.faces.ViewState'                      => $self->viewState,
            $selectName                                  => $selectName,
        }
    );

    my $parser = new BookPub::RDAS::Parser::POSData::Apple( content => $self->mechanize->content() );
    $self->{_viewState} = $parser->ViewState();

    unless ( $self->viewState ) {
        Log->error("  ** Failed to download sales report for date $reportDate");
        return;
    }

    $self->_postData(
        kReportURL,
        {
            theForm                                      => 'theForm',
            'theForm:xyz'                                => 'normal',
            'theForm:vendorType'                         => 'N',
            'theForm:optInVar'                           => 'A',
            'theForm:optInVarRender'                     => 'false',
            'theForm:wklyBool'                           => 'false',
            'theForm:datePickerSourceSelectElementSales' => $reportDate,
            'theForm:weekPickerSourceSelectElement'      => $self->sunday->asString('%m/%d/%Y'),
            'javax.faces.ViewState'                      => $self->viewState,
            'theForm:downloadLabel3'                     => 'theForm:downloadLabel3',
            $selectName                                  => $selectName,
        }
    );

    my $date     = Common::Date->new($reportDate)->asString();
    my $filename = $self->mechanize()->response()->filename();
    my $id       = $self->files->{$date};

    if ( $self->mechanize->content_type =~ /gzip/ ) {
        $self->{_reports}->{$date} = $self->_getFileFormObject(
            filename  => $filename,
            content   => $self->mechanize->content,
            startDate => $reportDate,
            posFileID => $id
        );
    } else {
        Log->error("  -- content returned not in gzip format for report date $reportDate");
        return;
    }

    return 1;
}

1;
