package WebService::BambooHR::UserAgent;
$WebService::BambooHR::UserAgent::VERSION = '0.06';
use 5.006;
use Moo::Role;
use HTTP::Tiny;
use MIME::Base64;
use WebService::BambooHR::Exception;

my $COMMA = ',';

has 'base_url' =>
    (
        is => 'ro',
        default => sub { return 'https://api.bamboohr.com/api/gateway.php'; },
    );

has 'api_key' =>
    (
        is       => 'ro',
    );

has 'ua' =>
    (
        is      => 'rw',
        default => sub { HTTP::Tiny->new(agent => "WebService-BambooHR/0.01"); },
    );

has 'company' =>
    (
        is       => 'ro',
    );

sub _get
{
    my $self     = shift;
    my $url      = shift;
    my $full_url = $self->base_url.'/'.$self->company.'/v1/'.$url;
    my $ua       = $self->ua;
    my $auth     = encode_base64($self->api_key.':x', '');
    my $headers  = { Accept => 'application/json', 'Authorization' => "Basic $auth" };
    my $response = $ua->request('GET', $full_url, { headers => $headers });
    my @caller   = caller(1);

    # If the public method has wrapped us in eval { ... }
    # then we need to go one level higher up the call stack
    @caller = caller(2) if $caller[3] eq '(eval)';

    if (not $response->{success}) {
        WebService::BambooHR::Exception->throw({
            method      => $caller[3],
            message     => "request to API failed",
            code        => $response->{status},
            reason      => $response->{reason},
            filename    => $caller[1],
            line_number => $caller[2],
        });
    }

    return $response;
}

sub _post
{
    my $self     = shift;
    my $url      = shift;
    my $content  = shift;
    my $full_url = $self->base_url.'/'.$self->company.'/v1/'.$url;
    my $ua       = $self->ua;
    my $auth     = encode_base64($self->api_key.':x', '');
    my $headers  = { Accept => 'application/json', 'Authorization' => "Basic $auth" };
    my $response = $ua->request('POST', $full_url, { headers => $headers, content => $content });
    my @caller   = caller(1);

    # If the public method has wrapped us in eval { ... }
    # then we need to go one level higher up the call stack
    @caller = caller(2) if $caller[3] eq '(eval)';

    if (not $response->{success}) {
        WebService::BambooHR::Exception->throw({
            method      => $caller[3],
            message     => "request to API failed",
            code        => $response->{status},
            reason      => $response->{reason},
            filename    => $caller[1],
            line_number => $caller[2],
        });
    }

    return $response;
}

my %field =
(
    address1                => q{The employee's first address line.},
    address2                => q{The employee's second address line.},
    age                     => q{The employee's age. To change age, update dateOfBirth field.},
    bestEmail               => q{The employee's work email if set, otherwise their home email.},
    birthday                => q{The employee's month and day of birth. To change birthday, update dateOfBirth field.},
    city                    => q{The employee's city.},
    country                 => q{The employee's country.},
    dateOfBirth             => q{The date the employee was born.},
    department              => q{The employee's CURRENT department.},
    division                => q{The employee's CURRENT division.},
    eeo                     => q{The employee's EEO job category. These are defined by the U.S. Equal Employment Opportunity Commission.},
    employeeAccess          => q{Whether the employee is allowed to access BambooHR.},
    employeeNumber          => q{Employee number (assigned by your company).},
    employmentStatus        => q{DEPRECATED. Please use "status" instead. The employee's employee status (Active or Inactive).},
    employmentHistoryStatus => q{The employee's CURRENT employment status. Options are customized by account.},
    ethnicity               => q{The employee's ethnicity.},
    exempt                  => q{The FLSA employee exemption code (Exempt or Non-exempt).},
    firstName               => q{The employee's first name.},
    flsaCode                => q{The employee's FLSA code (Exempt or Non-exempt).},
    fullName1               => q{The employee's first and last name. (e.g., John Doe). Read only.},
    fullName2               => q{The employee's last and first name. (e.g., Doe, John). Read only.},
    fullName3               => q{The employee's full name and their nickname. (e.g., Doe, John Quentin (JDog)). Read only.},
    fullName4               => q{The employee's full name without their nickname, last name first. (e.g., Doe, John Quentin). Read only.},
    fullName5               => q{The employee's full name without their nickname, first name first. (e.g., John Quentin Doe). Read only.},
    displayName             => q{The employee's name displayed in a format configured by the user. Read only.},
    gender                  => q{The employee's gender (Male or Female).},
    hireDate                => q{The date the employee was hired.},
    homeEmail               => q{The employee's home email address.},
    homePhone               => q{The employee's home phone number.},
    id                      => q{The employee ID automatically assigned by BambooHR. Read only.},
    jobTitle                => q{The CURRENT value of the employee's job title, updating this field will create a new row in position history.},
    lastChanged             => q{The date and time that the employee record was last changed.},
    lastName                => q{The employee's last name.},
    location                => q{The employee's CURRENT location.},
    maritalStatus           => q{The employee's marital status (Single, Married, or Domestic Partnership).},
    middleName              => q{The employee's middle name.},
    mobilePhone             => q{The employee's mobile phone number.},
    nickname                => q{The employee's nickname.},
    payChangeReason         => q{The reason for the employee's last pay rate change.},
    payGroup                => q{The custom pay group that the employee belongs to.},
    payGroupId              => q{The ID value corresponding to the pay group that an employee belongs to.},
    payRate                 => q{The employee's CURRENT pay rate (e.g., $8.25).},
    payRateEffectiveDate    => q{The day the most recent change was made.},
    payType                 => q{The employee's CURRENT pay type. ie: "hourly","salary","commission","exception hourly","monthly","weekly","piece rate","contract","daily","pro rata".},
    ssn                     => q{The employee's Social Security number.},
    sin                     => q{The employee's Canadian Social Insurance Number.},
    state                   => q{The employee's state/province.},
    stateCode               => q{The 2 character abbreviation for the employee's state (US only). Read only.},
    status                  => q{The employee's employee status (Active or Inactive).},
    supervisor              => q{The employee’s CURRENT supervisor. Read only.},
    supervisorId            => q{The 'employeeNumber' of the employee's CURRENT supervisor. Read only.},
    supervisorEId           => q{The ID of the employee's CURRENT supervisor. Read only.},
    terminationDate         => q{date the employee was terminated.},
    workEmail               => q{The employee's work email address.},
    workPhone               => q{The employee's work phone number, without extension.},
    workPhonePlusExtension  => q{The employee's work phone and extension. Read only.},
    workPhoneExtension      => q{The employee's work phone extension (if any).},
    zipcode                 => q{The employee's ZIP code.},
    photoUploaded           => q{The employee has uploaded a photo.},
    rehireDate              => q{The date the employee was rehired.},
    standardHoursPerWeek    => q{The number of hours the employee works in a standard week.},
    bonusDate               => q{The date of the last bonus.},
    bonusAmount             => q{The amount of the most recent bonus.},
    bonusReason             => q{The reason for the most recent bonus.},
    bonusComment            => q{Comment about the most recent bonus.},
    commissionDate          => q{The date of the last commission.},
    commisionDate           => q{This field name contains a typo, and exists for backwards compatibility.},
    commissionAmount        => q{The amount of the most recent commission.},
    commissionComment       => q{Comment about the most recent commission.},
    1360                    => q{The termination type when an employee has been terminated.},
    1361                    => q{The termination reason when an employee has been terminated.},
    1610                    => q{Employee self-service access.},
);

my %field_alias =
(
    selfServiceAccess => '1610',
    terminationType   => '1360',
    terminationReason => '1361',
);

sub _check_fields
{
    my $self        = shift;
    my @field_names = @_;
    my @caller      = caller(1);
    my @mapped_names;

    FIELD:
    foreach my $field_name (@field_names) {

        if (exists($field{$field_name})) {
            push(@mapped_names, $field_name);
            next FIELD;
        }
        elsif (exists($field_alias{$field_name})) {
            push(@mapped_names, $field_alias{ $field_name });
            next FIELD;
        }

        WebService::BambooHR::Exception->throw({
            method      => $caller[3],
            message     => "unknown field name '$field_name'",
            code        => 400,
            reason      => 'Bad Request',
            filename    => $caller[1],
            line_number => $caller[2],
        });

    }

    return @mapped_names;
}

sub _employee_record
{
    my $self      = shift;
    my $field_ref = shift;
    my %mapped_field;
    my $mapped_name;
    local $_;

    foreach my $field_name (keys %$field_ref) {
        $mapped_name = $field_alias{$field_name} if exists($field_alias{$field_name});
        $mapped_field{$mapped_name} = $field_ref->{ $field_name };
    }

    return "<employee>\n  "
                   .join("\n  ",
                         map { qq[<field id="$_">$mapped_field{$_}</field>] }
                         keys(%mapped_field)
                        )
                    ."\n"
          ."</employee>\n";
}

sub _field_list
{
    return keys %field;
}

1;

=head1 NAME

WebService::BambooHR::UserAgent - handles low-level HTTP requests to BambooHR

=head1 SYNOPSIS

 use WebService::BambooHR::UserAgent;

 my $ua = WebService::BambooHR::UserAgent->new(
                  company => 'foobar',
                  api_key => '.............'
              );
 
=head1 DESCRIPTION

This is used by L<WebService::BambooHR>, and most users shouldn't
even need to know it exists.

