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.14'; # 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.14

=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) 2023 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
