package Spike::Site::Handler;

use strict;
use warnings;

use base qw(Spike::Object);

use feature 'state';
use syntax 'try';

use FindBin;
use Carp;
use HTTP::Status qw(:constants);

use Spike::Error;
use Spike::Log;
use Spike::Site::Request;
use Spike::Config;

sub debug { state $debug //= $ENV{PLACK_ENV} =~ /devel/ }

sub run {
    my $proto = shift;
    my $class = ref $proto || $proto;

    state $self ||= $class->new(@_);

    return sub { $self->request(@_) };
}

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;

    my $self = $class->SUPER::new;

    $self->init(@_);

    return $self;
}

sub _error_page {
    my ($self, $status) = @_;

    return '<html><head><title>'.
        HTTP::Status::status_message($status).
        '</title></head><body bgcolor="white"><center><h1>'.
        $status.' '.HTTP::Status::status_message($status).
        '</h1></center><hr><center>Spike-'.$Spike::VERSION.
        '</center></body></html>';
}

sub request {
    my ($self, $env) = @_;

    $Spike::Log::bind_values = [ $$, $env->{REMOTE_ADDR} ];

    my $req = Spike::Site::Request->new($env);
    my $res = $req->new_response(HTTP_OK);

    try {
        $self->handler($req, $res);
    }
    catch (Spike::Error::HTTP $error) {
        carp "HTTP error: status=".$error->value.", text=\"".$error->text."\"";

        $res = $req->new_response($error->value, $error->headers,
            $self->_error_page($error->value));
    }
    catch (Spike::Error::HTTP_OK $error) {
        # do nothing
    }
    catch (Spike::Error $error) {
        carp "Error: class=".ref($error).", text=\"".$error->text."\"";

        $res = $req->new_response(HTTP_INTERNAL_SERVER_ERROR, undef,
            $self->_error_page(HTTP_INTERNAL_SERVER_ERROR));
    }
    catch ($error) {
        carp $error;

        $res = $req->new_response(HTTP_INTERNAL_SERVER_ERROR, undef,
            $self->_error_page(HTTP_INTERNAL_SERVER_ERROR));
    }

    $self->cleanup;

    $Spike::Log::bind_values = undef;

    return $res->finalize;
}

sub init {}

sub handler {}
sub cleanup {}

1;
