package BookPub::RDAS::Request::POSData::BarnesNoble;

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

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

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

use lib '/app/tools/bookpub/lib';
use BookPub::RDAS::Form::POSData::File::BarnesNoble;
use BookPub::RDAS::Response::POSData::BarnesNoble;
use BookPub::RDAS::Parser::POSData::BarnesNoble::Home;
use BookPub::DB::Item::POSFile;
use BookPub::ServiceCredential;

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

use constant kBaseURL => 'https://quickpub.barnesandnoble.com/servlet/bn';

# Number of days the sales reports go back.  Barnes has 31 days available, but
# we don't get today's because it contains incomplete information.
use constant kMaxDays => 30;

sub reports { shift->{_reports} }

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

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

sub _getFetchObject {
    my $self = shift;
    my $url  = shift || die;
    my $ua   = $self->userAgent;

    return RDAS::Fetcher::Mechanize::Authenticated->new(
        maxRedirect => 2,
        url         => $url,
        userAgent   => $ua,
    );
}

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;
    }

    $self->_initPOSFiles();

    # Now that all the initialization work is done lets actually do some real work
    unless ( $self->_storeMissingReports() ) {
        Log->error("Failed to store missing sales reports");
        $self->_logout();
        $self->_POSFileFail();
        return;
    }

    $self->_logout();
    $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 _login {
    my $self = shift;
    my ( $uname, $passwd ) = $self->_credentials();
    my %args = ( a => 'login', loginid => $uname, password => $passwd );
    my $fetcher = $self->_fetchPage( kBaseURL . "?t=home", form => \%args ) || return;

    my $parser = new BookPub::RDAS::Parser::POSData::BarnesNoble::Home( content => $fetcher->content() );
    if ( $parser->Body ) {
        Log->info(" ++ Login successful ++");
    } else {
        Log->error("Authentication failed");
        return;
    }

    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();

    return unless (@dates);

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

    return 1;
}

sub _fetchPage {
    my $self = shift;
    my $url  = shift;
    my %args = @_;

    my $fetcher = $self->_getFetchObject($url);

    if ( $fetcher->fetch(%args) ) {
        return $fetcher;
    } else {
        $self->{_error} = "Fetch failed.  HTTP Code: " . $fetcher->errorCode();
        return;
    }
}

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

    my $date      = Common::Date->now();
    my $startDate = $date->clone->addDays( -1 * ( kMaxDays + 1 ) );
    my $endDate   = $date->clone->addDays(-1);

    Log->info("  Start date: $startDate");
    Log->info("  End date: $endDate");

    my $reportDate = new Common::Date( $self->reportDate ) if ( $self->reportDate );

    if ($reportDate) {
        Log->info("  Report date: $reportDate");
        return $reportDate if ( $reportDate >= $startDate && $reportDate <= $endDate );

        Log->error("Report date $reportDate not available");
        return;
    } else {
        while ( $startDate <= $endDate ) {
            if ( !$self->feed->isPassedStartDate($startDate) ) {
                Log->info("  -- Ignoring date $startDate, not passed start date");
            }

            elsif ( !$self->feed->isNew($startDate) ) {
                Log->info("  -- Ignoring date $startDate, is older than last download");
            }

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

            $startDate->addDays(1);
        }

        return @dates;
    }
}

sub _downloadSalesReport {
    my $self       = shift;
    my $reportDate = shift || return;
    my $url        = kBaseURL;
    my $params;
    my %args = (
        a         => 'mv',
        t         => 'txt_salesbyday',
        startdate => $reportDate,
        enddate   => $reportDate,
        rpp       => 0,
        page      => 0
    );

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

    foreach my $key ( keys %args ) {
        $params .= $params ? '&' : '?';
        $params .= "$key=$args{$key}";
    }

    $url .= $params;

    my $fetcher = $self->_fetchPage($url);

    my $content  = $fetcher->content();
    my $filename = $fetcher->filename();

    Log->info("result filename: $filename");
    my $id = $self->files->{$reportDate};

    if ( $filename && $filename =~ /txt$/ ) {
        $self->{_reports}->{$reportDate} = $self->_getFileFormObject(
            filename  => $filename,
            content   => $content,
            startDate => $reportDate,
            posFileID => $id
        );
    } else {
        Log->error("  -- content returned not a valid sale report for date $reportDate");
        return;
    }

    return 1;
}

# Looks like B&N do some session management stuff.  If we don't logout after a request
# we need to wait for the session to expire before we download another report.
sub _logout {
    my $self = shift;
    my $url  = kBaseURL . "?a=logout&t=home";

    Log->notice("Logout");

    my $fetcher = $self->_fetchPage($url);

    return 1;
}

1;
