#!/usr/bin/perl -w

use strict;
use warnings;
use FindBin;
use lib ("$FindBin::Bin/../lib", "$FindBin::Bin/../extlib");
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";

use Getopt::Long;
use LWP::UserAgent;
use HTTP::Request;
use Web::Scraper;
use Data::Dumper;
use Time::HiRes;
use MT;
use MT::Blog;

# Set default option value
my @blog_ids;
my @templates;
my $entry_id  = 0;
my $count     = 1;
my $mt_url    = '';
my $username  = 'Melody';
my $password  = 'Nelson';
my $silent    = 0;
my $usage     = 0;

GetOptions(
    'blog_id=s'  => \@blog_ids,
    'template=s' => \@templates,
    'count=i'    => \$count,
    'mt_url=s'   => \$mt_url,
    'user=s'     => \$username,
    'pass=s'     => \$password,
    'silent'     => \$silent,
    'usage'      => \$usage,
);

# Construct MT object
my $mt = new MT
  or die "Not MT object " . MT->errstr();

# Validation of required options
if (   $usage
    || ( !$mt_url )
    || ( !$username )
    || ( !$password ) )
{
    usage();
    exit;
}

# Parse and load blog
if (@blog_ids) {
    @blog_ids = split(/,/, join(',', @blog_ids));
}
my @blogs = MT::Blog->load( { ( @blog_ids ? ( id => @blog_ids ) : () ), } )
    or die "Couldn't load blogs. [" . Dumper(@blog_ids) . "]\n";

# Parse template option
if (@templates) {
    @templates = split(/,/, join(',', @templates));
}

# Construct UserAgent object
my $ua = new LWP::UserAgent;

# Create scraper object
my $scraper = scraper {
    process "div.msg-publishing", status_publishing => 'TEXT';
    process "div.msg-error",      status_error      => 'TEXT';
    process "div.msg-success",    status_success    => 'TEXT';

    result 'status_publishing', 'status_error', 'status_success';
};

# call rebuild
for ( my $count_i = 0 ; $count_i < $count ; $count_i++ ) {
    foreach my $blog (@blogs) {
        logging( "rebuilding (" . ($count_i + 1) . ") " . $blog->name . " ...\n" );
        run_rebuild($blog);
    }
}

sub run_rebuild {
    my $blog = shift;

    my @at;
    if ( !@templates ) {
        @at = split( ',', $blog->archive_type );
        push @at, 'index';
    }
    else {
        @at = @templates;
    }

    foreach (@at) {
        my $at       = $_;
        my $archiver = $mt->publisher->archiver($at);
        next if ( !$archiver ) && ( $at ne 'index' );

        my $start = Time::HiRes::time();
        my $total = 0;
        if ($archiver) {
            if ( ( $archiver->entry_based || $archiver->date_based )
                && !$entry_id )
            {
                my $entry_class = $archiver->entry_class || 'entry';
                require MT::Entry;
                my $terms = {
                    class   => $entry_class,
                    status  => MT::Entry::RELEASE(),
                    blog_id => $blog->id,
                };
                $total = MT::Entry->count($terms);
            }
            elsif ( $archiver->category_based ) {
                require MT::Category;
                my $terms = {
                    blog_id => $blog->id,
                    class   => $archiver->category_class,
                };
                $total = MT::Category->count($terms);
            }
            elsif ( $archiver->author_based ) {
                require MT::Author;
                require MT::Entry;
                my $terms = {
                    blog_id => $blog->id,
                    status  => MT::Entry::RELEASE(),
                    class   => 'entry',
                };
                $total = MT::Author->count(
                    undef,
                    {
                        join => MT::Entry->join_on(
                            'author_id', $terms, { unique => 1 }
                        ),
                        unique => 1,
                    }
                );
            }
        }

        my $url =
            $mt_url
          . "?__mode=rebuild"
          . "&blog_id="
          . $blog->id
          . "&next=0"
          . "&offset=0"
          . "&limit=20"
          . "&entry_id="
          . "&is_new="
          . "&old_status="
          . "&old_previous="
          . "&old_next="
          . "&total="
          . $total
          . "&type="
          . $at;
        do {
            $url .= "&username=" . $username;
            $url .= "&password=" . $password;
            my $req = new HTTP::Request( GET => $url );
            my $resp = $ua->request($req);
            if ( $resp->is_success ) {
                my $res = $scraper->scrape( $resp->content() );
                if ( $res->{status_publishing} ) {
                    ( undef, $url ) =
                      $resp->content() =~ /window.location='(.*)\?(.*)'/;
                    $url = $mt_url . "?" . $url;
                }
                elsif ( $res->{status_success} ) {
                    logging( "\t" . $at . " built success." . "\n" );
                    $url = undef;
                }
                elsif ( $res->{status_error} ) {
                    logging("\t"
                          . $at
                          . " built failed. "
                          . $res->{status_error}
                          . "\n" );
                    $url = undef;
                }
                else {
                    logging("\t"
                          . $at
                          . " built failed.\n"
                          . $resp->content()
                          . "\n" );
                    $url = undef;
                }
            }
            else {
                logging(
                    "\t" . $at . " request failed (" . $resp->code . ")\n" );
                $url = undef;
            }
        } while ($url);

        logging( "\t  total build time:" . ( Time::HiRes::time() - $start ) . "\n" );
    }
}

sub logging {
    my ($msg) = @_;
    print $msg if !$silent;
}

sub usage {
    print STDERR << "EOT";
usage: $0
  require:
    -mt_url='URL to mt.cgi'
    -user='login account of mt'
    -pass='login password of mt'

  optional:
    -blog_id='target blog id.'
    -template='target archive type'
    -count='the count of cyclic call'
    -silent='no output any logs (1|0)'
    -usage='show this message'

* This script requires Web::Scraper. You must install from CPAN if you not installed yet.
EOT
}
