package CloudHealth::API::CallObjectFormer;
  use Moo;
  use HTTP::Tiny;
  use JSON::MaybeXS;
  use Module::Runtime qw/require_module/;
  use CloudHealth::API::Error;
  use CloudHealth::API::HTTPRequest;

  has _json => (is => 'ro', default => sub { JSON::MaybeXS->new });

  sub callinfo_class {
    my ($self, $call) = @_;
    my $class = "CloudHealth::API::Call::$call";
    require_module $class;
    return $class;
  }

  sub params2request {
    my ($self, $call, $creds, $user_params) = @_;

    my $call_object = eval { $self->callinfo_class($call)->new(@$user_params) };
    if ($@) {
      my $msg = $@;
      CloudHealth::API::Error->throw(
        type => 'InvalidParameters',
        message => "Error in parameters to method $call",
        detail => $msg,
      );
    }

    my $body_struct;
    if ($call_object->can('_body_params')) {
      $body_struct = {};
      foreach my $param (@{ $call_object->_body_params }) {
        my $key = $param->{ name };
        my $value = $call_object->$key;
        next if (not defined $value);

        my $location = defined $param->{ location } ? $param->{ location } : $key;
        $body_struct->{ $location } = $value;
      }
    }

    CloudHealth::API::Error->throw(
      type => 'NoCredentials',
      message => 'Cannot find credentials for the request'
    ) if (not $creds->is_set);

    my $params = {
      api_key => $creds->api_key,
    };
    foreach my $param (@{ $call_object->_query_params }) {
      my $key = $param->{ name };
      my $value = $call_object->$key;
      next if (not defined $value);

      my $location = defined $param->{ location } ? $param->{ location } : $key;
      $params->{ $location } = $value;
    }

    my $url_params = {};
    foreach my $param (@{ $call_object->_url_params }) {
      my $key = $param->{ name };
      my $value = $call_object->$key;
      next if (not defined $value);

      my $location = defined $param->{ location } ? $param->{ location } : $key;
      $url_params->{ $location } = $value;
    }
    my $url = $call_object->_url;
    $url =~ s/\:([a-z0-9_-]+)/$url_params->{ $1 }/ge;

    my $qstring = HTTP::Tiny->www_form_urlencode($params);
    my $req = CloudHealth::API::HTTPRequest->new;
    $req->method($call_object->_method);
    $req->url(
      "$url?$qstring",
    );
    $req->headers({
      (defined $body_struct) ? ('Content-Type' => 'application/json') : (),
      Accept => 'application/json',
    });
    $req->content($self->_json->encode($body_struct)) if (defined $body_struct);

    return $req;
  }
1;
