#!/usr/bin/env perl

#PODNAME: kanbanize-bugzilla-sync

use strict;
use Data::Dumper;

use Net::Bugzilla::Kanbanize;

use LWP::Simple;
use JSON;

use LWP::UserAgent;
use HTTP::Request;
use URI::Escape;
use List::MoreUtils qw(uniq);

use AppConfig;
my $config = AppConfig->new(
    "verbose",
    "kanbanize_apikey=s",
    "kanbanize_boardid=i",
    "bugzilla_token=s",

    # "bugzilla_url=s", { DEFAULT => "https://bugzilla.mozilla.org" },
    # "kanbanize_url=s", { DEFAULT => "http://kanbanize.com" },
);

$config->file("sync.cfg") if -r "sync.cfg";
$config->args();

print Dumper($config) if $config->verbose;

my $APIKEY = $config->kanbanize_apikey or die "Please configure an apikey";
my $BOARD_ID = $config->kanbanize_boardid
  or die "Please configure a kanbanize_boardid";
my $BUGZILLA_TOKEN = $config->bugzilla_token
  or die "Please configure a bugzilla_token";

my $ua = LWP::UserAgent->new();

$ua->timeout(15);
$ua->env_proxy;
$ua->default_header( 'apikey' => $APIKEY );

my @bugs;

use List::Util qw(shuffle);

if (@ARGV) {
    @bugs = @ARGV;
}
else {
    @bugs = shuffle get_bugs();
}

my $count = @bugs;

print STDERR "Found a total of $count bugs\n";

my $total = 0;
foreach my $bug (@bugs) {
    sync_bug($bug);
}

sub get_bugs {
    my $req =
      HTTP::Request->new( GET =>
"https://bugzilla.mozilla.org/rest/bug?token=$BUGZILLA_TOKEN&include_fields=id&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&component=WebOps%3A Bugzilla&component=WebOps%3A Community Platform&component=WebOps%3A Engagement&component=WebOps%3A IT-Managed Tools&component=WebOps%3A Labs&component=WebOps%3A Other&component=WebOps%3A Product Delivery&component=WebOps%3A SSL and Domain Names&product=Infrastructure %26 Operations"
      );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);    #$res->status_line;
    }

    my $data = decode_json( $res->decoded_content );

    my @bugs = map { $_->{id} } @{ $data->{bugs} };
    my @cards = get_bugs_from_all_cards();

    return uniq @bugs, @cards;
}

sub get_bugs_from_all_cards {

    my $req =
      HTTP::Request->new( POST =>
"http://kanbanize.com/index.php/api/kanbanize/get_all_tasks/boardid/$BOARD_ID/format/json"
      );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);    #$res->status_line;
    }

    my $cards = decode_json( $res->decoded_content );

    my @bugs;
    foreach my $card (@$cards) {
        my $extlink = $card->{extlink};    # XXX: Smarter parsing
        if ( $extlink =~ /(\d+)$/ ) {
            my $bugid = $1;
            push @bugs, $bugid;
        }
    }

    return @bugs;
}

sub sync_bug {
    my $bugid = shift;

    print STDERR "Bugid: $bugid\n";

    $total++;

    my $bug = get_bug_info($bugid);

    if ( not defined $bug ) {
        print STDERR "[$total/$count] No info for bug $bugid\n";
        return;
    }

    my $summary    = $bug->{summary};
    my $whiteboard = $bug->{whiteboard};

    my $card = parse_whiteboard($whiteboard);

    if ( not defined $card ) {
        $card = create_card($bug);

        if ( not $card ) {
            warn "Failed to create card for bug $bugid";
            return;
        }

        update_whiteboard( $bugid, $card->{taskid}, $whiteboard );
    }

    $card = retrieve_card( $card->{taskid} );

    my $cardid = $card->{taskid};
    print STDERR "[$total/$count] Card $cardid - Bug $bugid - $summary\n";

    #sync_bugzilla($bug, $card);
    sync_card( $card, $bug );
}

sub retrieve_card {
    my $card_id = shift;

    my $req =
      HTTP::Request->new( POST =>
"http://kanbanize.com/index.php/api/kanbanize/get_task_details/boardid/$BOARD_ID/taskid/$card_id/format/json"
      );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);    #$res->status_line;
    }

    my $card = decode_json( $res->decoded_content );

    return $card;
}

sub sync_bugzilla {

}

sub sync_card {
    my ( $card, $bug ) = @_;

    # Check Assignee
    my $bug_assigned  = $bug->{assigned_to};
    my $card_assigned = $card->{assignee};

    if (   defined $card_assigned
        && $card_assigned ne "None"
        && $bug_assigned =~ m/\@.*\.bugs$/ )
    {
        warn "Update bug assigned to $card_assigned";
        update_bug_assigned( $bug, $card_assigned );
    }
    elsif ($bug_assigned !~ m[^$card_assigned@]
        && $bug_assigned !~ m/\@.*\.bugs$/ )
    {
        warn "Update card assigned to $bug_assigned";

        #print STDERR
        # "bug_asigned: $bug_assigned card_assigned: $card_assigned\n";
        update_card_assigned( $card, $bug_assigned );
    }

    #Check summary (XXX: Formatting assumption here)
    my $bug_summary  = "$bug->{id} - $bug->{summary}";
    my $card_summary = $card->{title};

    if ( $bug_summary ne $card_summary ) {
        update_card_summary( $card, $bug_summary );
    }

    # Check status
    my $bug_status  = $bug->{status};
    my $card_status = $card->{columnname};

    # Close card on bug completion

    if ( $bug_status eq "RESOLVED" and $card_status ne "Done" ) {
        complete_card($card);
    }

    # XXX: Should we close bug on card completion?
    if ( $bug_status ne "RESOLVED" and $card_status eq "Done" ) {
        warn "Bug $bug->{id} is not RESOLVED but card says $card_status";
    }

    # Check extlink
    my $bug_link = "https://bugzilla.mozilla.org/show_bug.cgi?id=$bug->{id}";

    if ( $card->{extlink} ne $bug_link ) {
        update_card_extlink( $card, $bug_link );
    }
}

sub complete_card {
    my $card = shift;

    my $taskid = $card->{taskid};

    my $data = {
        boardid => $BOARD_ID,
        taskid  => $taskid,
        column  => 'Done',
    };

    my $req =
      HTTP::Request->new( POST =>
          "http://kanbanize.com/index.php/api/kanbanize/move_task/format/json"
      );

    $req->content( encode_json($data) );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);    #$res->status_line;
    }
}

sub update_card_extlink {
    my ( $card, $extlink ) = @_;

    my $taskid = $card->{taskid};

    my $data = {
        boardid => $BOARD_ID,
        taskid  => $taskid,
        extlink => $extlink,
    };

    my $req =
      HTTP::Request->new( POST =>
          "http://kanbanize.com/index.php/api/kanbanize/edit_task/format/json"
      );

    $req->content( encode_json($data) );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);    #$res->status_line;
    }
}

sub update_bug_assigned {
    my ( $bug, $assigned ) = @_;

    $assigned .= '@mozilla.com';

    my $bugid = $bug->{id};

    my $req =
      HTTP::Request->new(
        PUT => "https://bugzilla.mozilla.org/rest/bug/$bugid" );

    $req->content("assigned_to=$assigned&token=$BUGZILLA_TOKEN");

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);    #$res->status_line;
    }
}

sub update_card_summary {
    my ( $card, $bug_summary ) = @_;

    my $taskid = $card->{taskid};

    my $data = {
        boardid => $BOARD_ID,
        taskid  => $taskid,
        title   => $bug_summary,
    };

    my $req =
      HTTP::Request->new( POST =>
          "http://kanbanize.com/index.php/api/kanbanize/edit_task/format/json"
      );

    $req->content( encode_json($data) );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);    #$res->status_line;
    }
}

sub update_card_assigned {
    my ( $card, $bug_assigned ) = @_;

    my $taskid = $card->{taskid};
    ( my $assignee = $bug_assigned ) =~ s/\@.*//;

    my $req =
      HTTP::Request->new( POST =>
"http://kanbanize.com/index.php/api/kanbanize/edit_task/format/json/boardid/$BOARD_ID/taskid/$taskid/assignee/$assignee"
      );

    $req->content("[]");

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die $res->status_line;
    }

}

sub update_whiteboard {
    my ( $bugid, $cardid, $whiteboard ) = @_;

    my $req =
      HTTP::Request->new(
        PUT => "https://bugzilla.mozilla.org/rest/bug/$bugid" );

    $whiteboard =
      "[kanban:https://kanbanize.com/ctrl_board/$BOARD_ID/$cardid] $whiteboard";

    $req->content("whiteboard=$whiteboard&token=$BUGZILLA_TOKEN");

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die $res->status_line;
    }

}

sub create_card {
    my $bug = shift;

    my $data = {
        'summary' => "$bug->{id} - $bug->{summary}",
        'extlink' => "https://bugzilla.mozilla.org/show_bug.cgi?id=$bug->{id}",
        'boardid' => $BOARD_ID,
    };

    my $req =
      HTTP::Request->new( POST =>
"http://kanbanize.com/index.php/api/kanbanize/create_new_task/format/json"
      );

    $req->content( encode_json($data) );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        warn "can't create card:" . $res->status_line;
        die Dumper($res);
        return;
    }

    my $card = decode_json( $res->decoded_content );

    $card->{taskid} = $card->{id};

    move_card( $card, 'Pending Triage' );

    return $card;
}

sub move_card {
    my ( $card, $lane ) = @_;

    my $data = {
        boardid => $BOARD_ID,
        taskid  => $card->{taskid},
        column  => 'Backlog',
        lane    => $lane,
    };

    my $req =
      HTTP::Request->new( POST =>
          "http://kanbanize.com/index.php/api/kanbanize/move_task/format/json"
      );

    $req->content( encode_json($data) );

    my $res = $ua->request($req);

    if ( !$res->is_success ) {
        die Dumper($res);
    }

}

sub get_bug_info {
    my $bugid = shift;
    my $data =
      get("https://bugzilla.mozilla.org/rest/bug/$bugid?token=$BUGZILLA_TOKEN");

    if ( not $data ) {
        return;
    }

    $data = decode_json($data);

    return $data->{bugs}[0];
}

sub parse_whiteboard {
    my $whiteboard = shift;

    my $card;

    if ( $whiteboard =~
        m{\[kanban:https://kanbanize.com/ctrl_board/(\d+)/(\d+)\]} )
    {
        my $boardid = $1;
        my $cardid  = $2;

        $card = { taskid => $cardid };
    }

    return $card;
}

__END__

=pod

=encoding UTF-8

=head1 NAME

kanbanize-bugzilla-sync

=head1 VERSION

version 0.001

=head1 AUTHOR

Philippe M. Chiasson <gozer@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2014 by Philippe M. Chiasson.

This is free software, licensed under:

  Mozilla Public License Version 2.0

=cut
