package IO::Iron::Applications::Command::CommandBase;

## no critic (Documentation::RequirePodAtEnd)
## no critic (Documentation::RequirePodSections)
## no critic (RegularExpressions::RequireExtendedFormatting)
## no critic (RegularExpressions::RequireLineBoundaryMatching)
## no critic (RegularExpressions::ProhibitEscapedMetacharacters)

use 5.010_000;
use strict;
use warnings FATAL => 'all';

# Global creator
BEGIN {
    # Export nothing.
}

# Global destructor
END {
}

# ABSTRACT: Internal base class for CLI commands.

our $VERSION = '0.10'; # VERSION: generated by DZP::OurPkgVersion



use Log::Any  qw{$log};
require JSON::MaybeXS;
use Data::UUID ();
use Hash::Util 0.06 qw{lock_keys lock_keys_plus unlock_keys legal_keys};
use Carp::Assert;
use Carp::Assert::More;
use Carp;
use English '-no_match_vars';
use URI::Escape qw{uri_escape_utf8};
use Try::Tiny;
use Scalar::Util qw{blessed looks_like_number};
use Exception::Class (
      'IronHTTPCallException' => {
        fields => ['status_code', 'response_message'],
      }
  );

use IO::Iron::Common ();
use IO::Iron::Applications::IronCache::Functionality ();
use IO::Iron::Applications::IronCache::Templates ();

# CONSTANTS

use constant { ## no critic (ValuesAndExpressions::ProhibitConstantPragma)
    HTTP_CODE_OK_MIN => 200,
    HTTP_CODE_OK_MAX => 299,
    HTTP_CODE_SERVICE_UNAVAILABLE => 503,
};


sub description {
    return "Implemented in inheriting class.";
}


#sub abstract {
#    return "Show an IronCache";
#}


sub usage_desc { 
    return "Implemented in inheriting class.";
}


sub opt_spec_base {
    return (
        [ "help",    "This screen", { default => 0 } ],
        [ "config|c=s",    "Load Iron.io config from this file", ],
        [ "policies=s",     "Load policies from this file", ],
        [ "no-policy",    "Don't use policy to validate requests", { default => 0 }, ],
        [ "warning|warn!",    "Show warnings. Default: on. '--nowarn' to disable.", { default => 1 } ],
        [ "verbose|info",    "Give me information as things happen", { default => 0 } ],
        [ "debug",    "Give me more information as things happen", { default => 0 } ],
        [ "trace",    "Give me even more information as things happen", { default => 0 } ],
    );
}


sub validate_args_base {
    my ($self, $opt, $args) = @_;
    # we need at least one argument beyond the options; die with that message
    # and the complete "usage" text describing switches, etc
    $self->usage_error("Help Requested") if defined $opt->{'help'} && $opt->{'help'};
}


sub validate_args {
    my ($self, $opt, $args) = @_;
    # we need at least one argument beyond the options; die with that message
    # and the complete "usage" text describing switches, etc
    return "Implemented in inheriting class.";
}

use Log::Log4perl;
use Log::Log4perl::Level;
my $conf = q(
  log4perl.rootLogger          = ERROR, Screen
  log4perl.appender.Screen         = Log::Log4perl::Appender::Screen
  log4perl.appender.Screen.stderr  = 0
  log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout
);
Log::Log4perl::init( \$conf );
use Log::Any::Adapter;
Log::Any::Adapter->set('Log::Log4perl');


sub raise_logging_levels_from_options {
    my ($self, $opts) = @_;
    if($opts->{'warning'} > 0) {
        Log::Log4perl->get_logger("")->level($WARN);
        $log->info("Raised logging level to WARN.");
    }
    if($opts->{'verbose'} > 0) {
        Log::Log4perl->get_logger("")->level($INFO);
        $log->info("Raised logging level to INFO.");
    }
    if($opts->{'debug'} > 0) {
        Log::Log4perl->get_logger("")->level($DEBUG);
        $log->debug("Raised logging level to DEBUG.");
    }
    if($opts->{'trace'} > 0) {
        Log::Log4perl->get_logger("")->level($TRACE);
        $log->trace("Raised logging level to TRACE.");
    }
    return;
}


sub check_for_iron_io_config {
    my ($self, $opts) = @_;
    $log->tracef('Entering check_for_iron_io_config(%s)', $opts);
    my %params;
    $params{'config'} = $opts->{'config'} if defined $opts->{'config'};
    my $config = IO::Iron::Common::get_config(%params);
    if(! defined $config->{'project_id'}) {
        $log->fatalf("Missing config item \'project_id\'. Please check that Iron.io config is accessible (by .json file or environmental variables).");
        $log->tracef('Exiting check_for_iron_io_config():%d', -1);
        return -1;
    }
    $log->tracef('Exiting check_for_iron_io_config():[NOT DEFINED]');
    return
}

require Template;
$Template::Config::STASH = 'Template::Stash'; # Pure Perl implementation.
my %tt_config = (
    #INCLUDE_PATH => '/search/path',  # or list ref
    #INTERPOLATE  => 1,               # expand "$var" in plain text
    #POST_CHOMP   => 1,               # cleanup whitespace 
    #PRE_PROCESS  => 'header',        # prefix each template
    #EVAL_PERL  => 1,                # evaluate Perl code blocks
    # This is here to avoid the error "... used only once: possible typo ...":
    dummy_use_for_config_stash => $Template::Config::STASH,
);


sub combine_template {
    my ($self, $template_name, $data, $instructions) = @_;
    $log->tracef('Entering combine_template(%s,%s)', $template_name, $data);

    # Print with TT2
    my $tt = Template->new(\%tt_config);
    if(!$tt) {
        croak(Template->error());
    }
    my $tt_output;
    my $template_routine_name = 
            'IO::Iron::Applications::IronCache::Templates::' 
            . '_' . $template_name 
            . '_template';
    $log->debugf('combine_template(): Fetching template from \'%s\'', $template_routine_name);
    #my $tt_template = eval { $template_routine_name };
    my $tt_template;
    {
        no strict 'refs';
        $tt_template = &$template_routine_name();
    }
    $log->debugf('combine_template(): Found template:\'%s\'', $tt_template);
    my %tt_input = (
        'data' => $data,
        'instructions' => $instructions,
    );
    if(! ($tt->process(\$tt_template, \%tt_input, \$tt_output))) {
        $log->debug('\$template->error()=%s.\n', $tt->error());
        croak($tt->error());
    }
    $log->tracef('Exiting combine_template():%s', $tt_output);
    return $tt_output;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

IO::Iron::Applications::Command::CommandBase - Internal base class for CLI commands.

=head1 VERSION

version 0.10

=head1 SYNOPSIS

This package is for internal use of IO::Iron packages.

=head1 DESCRIPTION

This class object handles the actual http traffic. Parameters are 
passed from the calling object (partly from API class) via Connection
class object. This class can be mocked and replaced when
the client objects are created.

=head1 SUBROUTINES/METHODS

=head2 description 

Describe the command.

=head2 abstract

This method returns a short description of the command's purpose.
If this method is not overridden, it will return the abstract 
from the module's Pod. If it can't find the abstract, it will look 
for a comment starting with "ABSTRACT:" like 
the ones used by Pod::Weaver::Section::Name.

=head2 usage_desc

Describe usage of the command.

=head2 opt_spec_base

The options shared by all sub commands.

=head2 validate_args_base

Call this in the inheriting classes' validate_args method before any other validation.

=head2 validate_args

Validate the arguments (and options/flags) if required.

=head2 raise_logging_levels_from_options

Raise logging level according to specified options.
Available options: info, debug, trace.

=head2 check_for_iron_io_config

Check that config can be accessed.

=head2 combine_template

Find template (args[1]) and combine it with data in the referenced structure (args[2]).

=head1 AUTHOR

Mikko Koivunalho <mikko.koivunalho AT iki.fi>

=head1 BUGS

Please report any bugs or feature requests to bug-io-iron-applications@rt.cpan.org or through the web interface at:
 http://rt.cpan.org/Public/Dist/Display.html?Name=IO-Iron-Applications

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by Mikko Koivunalho.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

The full text of the license can be found in the
F<LICENSE> file included with this distribution.

=cut
