package App::Web;

use strict;
use Log::Log4perl     qw( get_logger );
use Workflow::Factory qw( FACTORY );
use XML::Simple       qw( :strict );

my ( $log );

my %ACTION_DATA = ();
my %DISPATCH    = ();

sub initialize_mappings {
    my ( $class, $mapping_file ) = @_;
    $log ||= get_logger();
    my %options = (
        ForceArray => [ 'url-mapping', 'action-display' ],
        KeyAttr    => [],
    );
    my $config = XMLin( $mapping_file, %options );
    no strict 'refs';
    foreach my $url_map ( @{ $config->{'url-mapping'} } ) {
        my $map_class  = $url_map->{class};
        my $map_method = $url_map->{method};
        eval "require $map_class";
        if ( $@ ) {
            die "Cannot include class '$map_class': $@\n";
        }
        my $method = \&{ $map_class . '::' . $map_method };
        unless ( $method ) {
            die "No method '$map_class->$map_method'\n";
        }
        $DISPATCH{ $url_map->{url} } = $method;
    }

    foreach my $action_template ( @{ $config->{'action-display'} } ) {
        $ACTION_DATA{ $action_template->{name} } = $action_template->{template};
    }

    return $config;
}


########################################
# DISPATCH MAPPINGS
#
# Each of these routines returns a template name, stuffing data used
# by the template into \%params and any outbound cookies into
# \%cookies_out.

sub lookup_dispatch {
    my ( $class, $action_name ) = @_;
    return $DISPATCH{ $action_name };
}

sub create_workflow {
    my ( $client, $request, $cookies_in, $cookies_out, $params ) = @_;
    my $wf = FACTORY->create_workflow( 'Ticket' );
    $params->{workflow} = $wf;
    $cookies_out->{workflow_id} = $wf->id;
    return 'workflow_created.tmpl';
}

sub fetch_workflow {
    my ( $client, $request, $cookies_in, $cookies_out, $params ) = @_;
    my $wf = _get_workflow( $params, $cookies_in );
    $cookies_out->{workflow_id} = $wf->id;
    return 'workflow_fetched.tmpl';
}

sub list_history {
    my ( $client, $request, $cookies_in, $cookies_out, $params ) = @_;
    my $wf = _get_workflow( $params, $cookies_in );
    my @history = $wf->get_history();
    $params->{history_list} = \@history;
    return 'workflow_history.tmpl';
}

sub execute_action {
    my ( $client, $request, $cookies_in, $cookies_out, $params ) = @_;
    my $wf = _get_workflow( $params, $cookies_in );

    my $action = $params->{action};
    unless ( $action ) {
        die "To execute an action you must specify an action name!\n";
    }

    # If they haven't entered data yet, add the fields (as a map) to
    # the parameters and redirect to the form for entering it

    unless ( $params->{_action_data_entered} || ! $ACTION_DATA{ $action } ) {
        $params->{status_msg} =
            "Action cannot be executed until you enter its data";
        my @fields = $wf->get_action_fields( $action );
        my %by_name = map { $_->name => $_ } @fields;
        $params->{ACTION_FIELDS} = \%by_name;
        return $ACTION_DATA{ $action };
    }

    # Otherwise, set the user data directly into the workflow context...
    $wf->context->param( $params );

    # ...and execute the action
    eval { $wf->execute_action( $params->{action} ) };

    # ...if we catch a condition/validation exception, display the
    # error and go back to the data entry form

    if ( $@ && ( $@->isa( 'Workflow::Exception::Condition' ) ||
                 $@->isa( 'Workflow::Exception::Validation' ) ) ) {
        $log->error( "One or more conditions not met to execute action: $@; ",
                     "redirecting to form" );
        $params->{error_msg} = "Failed to execute action: $@";
        return $ACTION_DATA{ $action };
    }
    $params->{status_msg} = "Action '$action' executed ok";
    return list_history( $client, $request, $cookies_in, $cookies_out, $params );
}

sub login {
    my ( $client, $request, $cookies_in, $cookies_out, $params ) = @_;
    if ( $params->{current_user} ) {
        $cookies_out->{current_user} = $params->{current_user};
    }
    else {
        $params->{error_msg} = "Please specify a login name I can use!";
    }
    return 'index.tmpl';
}

sub _get_workflow {
    my ( $params, $cookies_in ) = @_;
    return $params->{workflow} if ( $params->{workflow} );
    my $log = get_logger();
    my $wf_id = $params->{workflow_id} || $cookies_in->{workflow_id};
    unless ( $wf_id ) {
        die "No workflow ID given! Please fetch a workflow or create ",
            "a new one.\n";
    }
    $log->debug( "Fetching workflow with ID '$wf_id'" );
    my $wf = FACTORY->fetch_workflow( 'Ticket', $wf_id );
    if ( $wf ) {
        $log->debug( "Workflow found, current state is '", $wf->state, "'" );
        $params->{workflow} = $wf;
    }
    else {
        $log->warn( "No workflow found with ID '$wf_id'" );
        die "No workflow found with ID '$wf_id'\n";
    }
    $log->info( "Setting current user to: $cookies_in->{current_user}" );
    $wf->context->param( current_user => $cookies_in->{current_user} );
    if ( my $ticket_id = $wf->context->param( 'ticket_id' ) ) {
        my $ticket = App::Ticket->fetch( $ticket_id );
        $log->info( "Setting ticket '", $ticket->id, "' to context" );
        $wf->context->param( ticket => $ticket );
    }
    return $wf;
}

1;
