package IO::Iron::Connection;

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

use 5.010_000;
use strict;
use warnings;

# Global creator
BEGIN {
	# No exports.
}

# Global destructor
END {
}


# ABSTRACT: Internet connection reference!

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



use Log::Any  qw{$log};
use Hash::Util 0.06 qw{lock_keys unlock_keys};
use Carp::Assert;
use Carp::Assert::More;
use English '-no_match_vars';


# DEFAULTS
use Const::Fast;
const my $DEFAULT_PROTOCOL => 'https';
const my $DEFAULT_PORT => 443;
const my $DEFAULT_TIMEOUT => 3;
;



sub new {
	my ($class, $params) = @_;
	$log->tracef('Entering new(%s, %s)', $class, $params);
	my $self;
	my @self_keys = ( ## no critic (CodeLayout::ProhibitQuotedWordLists)
			'project_id',    # The ID of the project to use for requests.
			'token',         # The OAuth token that should be used to authenticate requests. Can be found in the HUD.
			'host',          # The domain name the API can be located at. Defaults to a product-specific value, but always using Amazon's cloud.
			'protocol',      # The protocol that will be used to communicate with the API. Defaults to "https", which should be sufficient for 99% of users.
			'port',          # The port to connect to the API through. Defaults to 443, which should be sufficient for 99% of users.
			'api_version',   # The version of the API to connect through. Defaults to the version supported by the client. End-users should probably never change this. Except: IronMQ service upgraded from v2 to v3 in 2015!
			'timeout',       # REST client timeout (for REST calls accessing Iron services)
			'connector',     # Reference to the object which does the actual REST client calls, or mocks them.
	);
	lock_keys(%{$self}, @self_keys);
	$log->debugf('The params: %s', $params);
	$self->{'project_id'} = defined $params->{'project_id'} ? $params->{'project_id'} : undef;
	$self->{'token'} = defined $params->{'token'} ? $params->{'token'} : undef;
	$self->{'host'} = defined $params->{'host'} ? $params->{'host'} : undef;
	$self->{'protocol'} = defined $params->{'protocol'} ? $params->{'protocol'} : $DEFAULT_PROTOCOL;
	$self->{'port'} = defined $params->{'port'} ? $params->{'port'} : $DEFAULT_PORT;
	$self->{'api_version'} = defined $params->{'api_version'} ? $params->{'api_version'} : undef;
	$self->{'timeout'} = defined $params->{'timeout'} ? $params->{'timeout'} : $DEFAULT_TIMEOUT;
	# Set up the connector object.
	if(defined $params->{'connector'}) {
		$self->{'connector'} = $params->{'connector'}; # The connector has been instantiated for us.
	}
	else {
		require IO::Iron::Connector;
		$self->{'connector'} = IO::Iron::Connector->new();
	}

	unlock_keys(%{$self});
	bless $self, $class;
	lock_keys(%{$self}, @self_keys);

	#$self->_assert_configuration($self);

	$log->infof('IO::Iron::Connection client created with config: (project_id=%s; token=%s; host=%s; protocol=%s; port=%s; api_version=%s; timeout=%s).',
		$self->{'project_id'}, $self->{'token'}, $self->{'host'}, $self->{'protocol'}, $self->{'port'}, $self->{'api_version'}, $self->{'timeout'});
	$log->tracef('Exiting new: %s', $self);
	return $self;
}


sub perform_iron_action {
	my ($self, $iron_action, $params) = @_;
	if(!defined $params) {
		$params = {};
	}
	$log->tracef('Entering perform_iron_action(%s, %s)', $iron_action, $params);
	$self->_assert_configuration();

	my $href = $iron_action->{'href'};
	my $action_verb = $iron_action->{'action'};
	my $retry = $iron_action->{'retry'};
	my $require_body = $iron_action->{'require_body'};
	my $paged = $iron_action->{'paged'} ? $iron_action->{'paged'} : 0;
	my $per_page = $iron_action->{'per_page'} ? $iron_action->{'per_page'} : 0;
	my $log_message = $iron_action->{'log_message'} ? $iron_action->{'log_message'} : q{};
	my $request_fields = $iron_action->{'request_fields'} ? $iron_action->{'request_fields'} : {};
	my $content_type = $iron_action->{'content_type'};

	$params->{'{Protocol}'} = $self->{'protocol'};
	$params->{'{Port}'} = $self->{'port'};
	$params->{'{Host}'} = $self->{'host'};
	$params->{'{Project ID}'} = $self->{'project_id'};
	$params->{'{API Version}'} = $self->{'api_version'};
	$params->{'authorization_token'} = $self->{'token'};
	$params->{'http_client_timeout'} = $self->{'timeout'};
	$params->{'content_type'} = $content_type;

	my $connector = $self->{'connector'};
	my ($http_status_code, $returned_msg) = $connector->perform_iron_action($iron_action, $params);

	# Logging
	foreach my $key (sort keys %{$params}) {
		my $value = $params->{$key};
		$log_message =~ s/$key/$value/gs; ## no critic (RegularExpressions::RequireExtendedFormatting)
	};
	foreach my $key (sort keys %{$request_fields}) {
		my $field_name = $request_fields->{$key};
		my $field_value = $params->{'body'}->{$key} ? $params->{'body'}->{$key} : q{};
		$log_message =~ s/$field_name/$field_value/gs; ## no critic (RegularExpressions::RequireExtendedFormatting)
	};
	$log->info($log_message);
	$log->tracef('Exiting perform_iron_action(): %s', $returned_msg );
	return $http_status_code, $returned_msg;
}

# INTERNAL METHODS

# Assert that all the configuration is valid before making any network operation.
sub _assert_configuration {
	my ($self) = @_;
	$log->tracef('Entering _assert_configuration(%s)', $self);

	my $rval = 1;
	assert_nonblank( $self->{'project_id'}, 'self->{project_id} is defined and not blank.' );
	assert_nonblank( $self->{'token'}, 'self->{token} is defined and not blank.' );
	assert_nonblank( $self->{'host'}, 'self->{host} is defined and not blank.' );
	assert_nonblank( $self->{'protocol'}, 'self->{protocol} is defined and not blank.' );
	assert_nonblank( $self->{'port'}, 'self->{port} is defined and not blank.' );
	assert_nonblank( $self->{'api_version'}, 'self->{api_version} is defined and not blank.' );
	#assert_nonblank( $self->{'timeout'}, 'self->{timeout} is defined and not blank.' );
	assert_nonnegative_integer( $self->{'timeout'}, 'self->{timeout} is a nonnegative integer.' );
	assert_isa( $self->{'connector'}, 'IO::Iron::ConnectorBase', 'self->{connector} is a descendant of IO::Iron::ConnectorBase.' );

	$log->tracef('Exiting _assert_configuration(): %d', $rval);
	return $rval;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

IO::Iron::Connection - Internet connection reference!

=head1 VERSION

version 0.13

=head1 SYNOPSIS

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

=head1 DESCRIPTION

=for stopwords Params IronMQ params Mikko Koivunalho ACKNOWLEDGMENTS TODO

=head1 SUBROUTINES/METHODS

=head2 new

Creator function.

=head2 perform_iron_action

=over 8

=item Params: action name, params hash.

=item Return: 1/0 (1 if success, 0 in all failures),
HTTP return code, hash if success/failed request.

=back

=head1 AUTHOR

Mikko Koivunalho <mikko.koivunalho@iki.fi>

=head1 BUGS

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

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 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
