#!/usr/bin/perl

# Make debug console application on your browser
# DO NOT PUBLISH THIS APPLICATION TO THE INTERNET

use strict;
use warnings;
use Data::Dumper;
use Plack::Request;
use Plack::Builder;
use Text::MicroTemplate qw( render_mt );
use WWW::TypePad;

# These should be set to the Consumer Key and Consumer Secret
# for your TypePad application. You can obtain these values from
# http://www.typepad.com/account/access/api_key.
our $ConsumerKey = $ENV{TP_CONSUMER_KEY};
our $ConsumerSecret = $ENV{TP_CONSUMER_SECRET};

sub error {
    my( $code, $html ) = @_;
    return [
        $code,
        [ 'Content-Type', 'text/html' ],
        [ $html ],
    ];
}

sub Plack::Request::uri_for {
    my $req = shift;
    my( $path ) = @_;
    $path = '/' . $path unless $path =~ m{^/};
    return 'http://' . $req->env->{HTTP_HOST} . $path;
}

sub tp {
    my $tp = WWW::TypePad->new(
        consumer_key        => $ConsumerKey,
        consumer_secret     => $ConsumerSecret,
    );
    $tp->host($ENV{TP_API_HOST}) if $ENV{TP_API_HOST};
    $tp;
}

my $home = sub {
    my $req = Plack::Request->new( shift );
    my $tp = tp();

    # Do we have a logged-in user? If so, make an API request to pull down
    # the user's profile info. Not that we're not using the access token
    # and access token secret here, since this is a public API; but the
    # access token and secret that we stored in the session could be used
    # to make authenticated requests acting on behalf of the logged-in user.
    my $session = $req->session;
    my $obj;
    if ( $session && $session->{user} ) {
        $tp->access_token( $session->{user}{token} );
        $tp->access_token_secret( $session->{user}{token_secret} );
        $obj = $tp->users->get( $session->{user}{xid} );
    }

    local $Data::Dumper::Indent = 1;

    my $code = $req->parameters->{code};
    my($result, $err, $warn);
    if ($code && $obj) {
        # WHOAH
        my $user = $obj; # alias
        local $SIG{__WARN__} = sub { $warn .= $_[0] };
        $result = eval "no strict; $code";
        $err = $@ if $@;
    } else {
        $code = <<'CODE';
# $tp is WWW::TypePad object
# $user is User
CODE
    }

    my $html = render_mt( <<'HTML', $obj, $code, $result, $err, $warn );
? my($user, $code, $res, $err, $warn) = @_;
<html>
<head>
    <title>TypePad API debug console</title>
    <style type="text/css">
    body { font-family: Helvetica, Arial, Verdana, sans-serif; text-align: center; color: #333; }
    h3 { margin-top: 60px; font-size: 48px; }
    h5 { font-size: 12px; }
    .error { font-color: #f00 }
    .console { width: 500px }
    </style>
</head>
<body>
? if ( $user ) {
    <h3>Welcome back, <a href="http://profile.typepad.com/<?= $user->{urlId} ?>"><?= $user->{displayName} ?></a>!</h3>
    <form action="/" method="post">
      <textarea name="code" rows="8" cols="80"><?= $code ?></textarea>
      <br/><input type="submit" value=" run "/>
    </form>
?   if ( $res ) {
      <textarea rows="16" cols="80" class="dump"><?= Dumper $res ?></textarea>
?   } elsif ( $err ) {
      <textarea rows="2" cols="80"><?= $err ?></textarea>
?   }
?   if ( $warn ) {
      <textarea rows="8" cols="80"><?= $warn ?></textarea>
?   }
    <h5><a href="/logout">(Sign out)</a></h5>
? } else {
    <h3><a href="/login">Sign in</a></h3>
? }
</body>
</html>
HTML

    my $res = Plack::Response->new( 200 );
    $res->content_type( 'text/html' );
    $res->body( $html );
    return $res->finalize;
};

my $login = sub {
    my $req = Plack::Request->new( shift );
    my $tp = tp();

    # After the user authorizes our application, he/she will be sent back
    # to the callback URI ($login_cb below).
    my $cb_uri = $req->uri_for( '/login-callback' );

    # Under the hood, get_authorization_url will request a request token,
    # then construct a URI to send the user to to authorize our app.
    my $uri = $tp->oauth->get_authorization_url(
        callback => $cb_uri,
    );

    my $res = Plack::Response->new;
    $res->redirect( $uri );

    # Store the token secret in the browser cookies for when this user
    # returns from TypePad.
    $res->cookies->{oauth_token_secret} = $tp->oauth->request_token_secret;

    return $res->finalize;
};
 
my $login_cb = sub {
    my $req = Plack::Request->new( shift );
    my $tp = tp();

    # request_token is passed back to us via the query string
    # as "oauth_token"...
    my $token = $req->query_parameters->{oauth_token}
        or return error( 400, 'No oauth_token' );

    # ... and the request_token_secret is stored in the browser cookie.
    my $token_secret = $req->cookies->{oauth_token_secret}
        or return error( 400, 'No oauth_token_secret cookie' );

    my $verifier = $req->query_parameters->{oauth_verifier}
        or return error( 400, 'No oauth_verifier' );

    $tp->oauth->request_token( $token );
    $tp->oauth->request_token_secret( $token_secret );

    # Given the request token, token secret, and verifier that TypePad
    # sent us, request an access token and secret that we can use for
    # future authenticated calls on behalf of this user.
    my( $access_token, $access_token_secret ) =
        $tp->oauth->request_access_token( verifier => $verifier );
    $tp->access_token( $access_token );
    $tp->access_token_secret( $access_token_secret );

    # Now we've got an access token; make an authenticated request to figure
    # out who we are, so we can associate the OAuth tokens to a local user.
    my $obj = $tp->users->get( '@self' );
    return error( 500, 'Request for @self gave us empty result' )
        unless $obj;

    # And store the user's xid, the access token, and access token
    # secret in a session. In a real application, we'd store these
    # in an actual datastore.
    $req->session->{user} = {
        xid             => $obj->{urlId},
        token           => $access_token,
        token_secret    => $access_token_secret,
    };

    my $res = Plack::Response->new;
    $res->redirect( $req->uri_for( '/' ) );

    # Remove the request token secret cookie that we created above.
    $res->cookies->{oauth_token_secret} = {
        value   => '',
        expires => time - 24 * 60 * 60,
    };

    return $res->finalize;
};

my $logout = sub {
    my $req = Plack::Request->new( shift );

    # Kill the session.
    $req->env->{'psgix.session'} = {};

    my $res = Plack::Response->new;
    $res->redirect( $req->uri_for( '/' ) );
    return $res->finalize;
};

builder {
    # Sign session cookies using our API secret. You could use something
    # else, if you want, but it's highly recommended to sign cookies with
    # some secret.
    enable 'Session::Cookie', secret => $ConsumerSecret;

    mount '/' => $home;
    mount '/login' => $login;
    mount '/login-callback' => $login_cb;
    mount '/logout' => $logout;

    # Kill favicon requests.
    mount '/favicon.ico' => sub { return error( 404, "not found" ) };
};