package WWW::LogicBoxes;

use strict;
use warnings;

use Moose;
use MooseX::StrictConstructor;
use MooseX::Aliases;
use namespace::autoclean;

use WWW::LogicBoxes::Types qw( Bool ResponseType Str URI );

use Data::Util qw( is_hash_ref );
use Carp;

our $VERSION = '1.2.0'; # VERSION
# ABSTRACT: Interact with LogicBoxes reseller API

use Readonly;
Readonly my $LIVE_BASE_URI => 'https://httpapi.com';
Readonly my $TEST_BASE_URI => 'https://test.httpapi.com';

has username => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

has password => (
    is        => 'ro',
    isa       => Str,
    required  => 0,
    predicate => 'has_password',
);

has api_key => (
    is        => 'ro',
    isa       => Str,
    required  => 0,
    alias     => 'apikey',
    predicate => 'has_api_key',
);

has sandbox => (
    is      => 'ro',
    isa     => Bool,
    default => 0,
);

has response_type => (
    is       => 'rw',
    isa      => ResponseType,
    default  => 'xml',
);

has _base_uri => (
    is       => 'ro',
    isa      => URI,
    lazy     => 1,
    builder  => '_build_base_uri',
);

with 'WWW::LogicBoxes::Role::Command';

around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;
    my %args = @_ == 1 && is_hash_ref( $_[0] ) ? %{ $_[0] } : @_;

    # Assign since api_key or apikey are both valid due to backwards compaitability
    my $password = $args{password};
    my $api_key  = $args{apikey} // $args{api_key};

    if( !$password && !$api_key ) {
        croak 'A password or api_key must be specified';
    }

    if( $password && $api_key ) {
        croak "You must specify a password or an api_key, not both";
    }

    return $class->$orig(%args);
};

## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _build_base_uri {
    my $self = shift;

    return $self->sandbox ? $TEST_BASE_URI : $LIVE_BASE_URI;
}
## use critic

1;

__END__
=pod

=head1 NAME

WWW::LogicBoxes - Interact with LogicBoxes Reseller API

=head1 SYNOPSIS

    use strict;
    use warnings;

    use WWW::LogicBoxes;

    my $logic_boxes = WWW::LogicBoxes->new(
        username      => 'resellerid',

        # You may specify a password OR an api_key
        password      => 'Top S3cr3t!',
        api_key       => 'reseller_api_key',

        response_type => 'json',
        sandbox       => 0,
    );

    my $domain_availabilities = $logic_boxes->check_domain_availability(
        slds => [qw( cpan drzigman brainstormincubator ],
        tlds => [qw( com net org )],
        suggestions => 0,
    );

=head1 DESCRIPTION

L<WWW::LogicBoxes> is a module for interacting with the L<LogicBoxes|http://www.logicboxes.com/> API.  LogicBoxes is a domain registrar and the API performs operations such as checking domain availability, purchasing domains, and managing them.

This module is broken down into two primary components (documented below).  These are L<WWW::LogicBoxes/COMMANDS> which are used for making requests and L<WWW::LogicBoxes/OBJECTS> which are used to represent data.  Below these, documentation for the L<WWW::LogicBoxes> module is included.

=head1 COMMANDS

Commands are how operations are performed using the L<WWW::LogicBoxes> API.  They are seperated into related operations, for documentation on the specific command please see the linked pages.

=head2 L<Raw|WWW::LogicBoxes::Role::Command::Raw>

Low level direct access to the LogicBoxes API.  You rarely want to make use of this and instead want to use the abstracted commands outlined below.

=head2 L<Customer|WWW::LogicBoxes::Role::Command::Customer>

Customer creation and retrieval.  All domains belong to a customer.

=over 4

=item L<create_customer|WWW::LogicBoxes::Role::Command::Customer/create_customer>

=item L<get_customer_by_id|WWW::LogicBoxes::Role::Command::Customer/get_customer_by_id>

=item L<get_customer_by_username|WWW::LogicBoxes::Role::Command::Customer/get_customer_by_username>

=back

=head2 L<Contact|WWW::LogicBoxes::Role::Command::Contact>

Contacts are used in whois information and are required for domain registration.

=over 4

=item L<create_contact|WWW::LogicBoxes::Role::Command::Contact/create_contact>

=item L<get_contact_by_id|WWW::LogicBoxes::Role::Command::Contact/get_contact_by_id>

=item L<update_contact|WWW::LogicBoxes::Role::Command::Contact/update_contact>

=item L<delete_contact_by_id|WWW::LogicBoxes::Role::Command::Contact/delete_contact_by_id>

=back

=head2 L<Domain Availability|WWW::LogicBoxes::Role::Command::Domain::Availability>

Used for checking to see if a domain is available for registration as well as getting suggestions of other potentially relevant domains.

=over 4

=item L<check_domain_availability|WWW::LogicBoxes::Role::Command::Domain::Availability/check_domain_availability>

=item L<suggest_domain_names|WWW::LogicBoxes::Role::Command::Domain::Availability/suggest_domain_names>

=back

=head2 L<Domain Registration|WWW::LogicBoxes::Role::Command::Domain::Registration>

New Domain Registration.

=over 4

=item L<register_domain|WWW::LogicBoxes::Role::Command::Domain::Registration/register_domain>

=item L<delete_domain_registration_by_id|WWW::LogicBoxes::Role::Command::Domain::Registration/delete_domain_registration_by_id>

=back

=head2 L<Domain Transfer|WWW::LogicBoxes::Role::Command::Domain::Transfer>

New Domain Transfers.

=over 4

=item L<is_domain_transferable|WWW::LogicBoxes::Role::Command::Domain::Transfer/is_domain_transferable>

=item L<transfer_domain|WWW::LogicBoxes::Role::Command::Domain::Transfer/transfer_domain>

=item L<delete_domain_transfer_by_id|WWW::LogicBoxes::Role::Command::Domain::Transfer/delete_domain_transfer_by_id>

=item L<resend_transfer_approval_mail_by_id|WWW::LogicBoxes::Role::Command::Domain::Transfer/resend_transfer_approval_mail_by_id>

=back

=head2 L<Domain|WWW::LogicBoxes::Role::Command::Domain>

Retrieval of and management of registered domains.

=over 4

=item L<get_domain_by_id|WWW::LogicBoxes::Role::Command::Domain/get_domain_by_id>

=item L<get_domain_by_name|WWW::LogicBoxes::Role::Command::Domain/get_domain_by_name>

=item L<update_domain_contacts|WWW::LogicBoxes::Role::Command::Domain/update_domain_contacts>

=item L<enable_domain_lock_by_id|WWW::LogicBoxes::Role::Command::Domain/enable_domain_lock_by_id>

=item L<disable_domain_lock_by_id|WWW::LogicBoxes::Role::Command::Domain/disable_domain_lock_by_id>

=item L<update_domain_nameservers|WWW::LogicBoxes::Role::Command::Domain/update_domain_nameservers>

=item L<renew_domain|WWW::LogicBoxes::Role::Command::Domain/renew_domain>

=back

=head2 L<Domain Private Nameservers|WWW::LogicBoxes::Role::Command::Domain::PrivateNameServer>

I<Private> nameservers are those that are based on the registered domain.  For example, a domain of test-domain.com could have private nameservers ns1.test-domain.com and ns2.test-domain.com.

=over 4

=item L<create_private_nameserver|WWW::LogicBoxes::Role::Command::Domain::PrivateNameServer/create_private_nameserver>

=item L<rename_private_nameserver|WWW::LogicBoxes::Role::Command::Domain::PrivateNameServer/rename_private_nameserver>

=item L<modify_private_nameserver_ip|WWW::LogicBoxes::Role::Command::Domain::PrivateNameServer/modify_private_nameserver_ip>

=item L<delete_private_nameserver_ip|WWW::LogicBoxes::Role::Command::Domain::PrivateNameServer/delete_private_nameserver_ip>

=item L<delete_private_nameserver|WWW::LogicBoxes::Role::Command::Domain::PrivateNameServer/delete_private_nameserver>

=back

=head1 OBJECTS

Rather than working with messy JSON objects, WWW::LogicBoxes implements a series of L<Moose> objects for making requests and processing responses.  All commands that take an object have coercion so a HashRef can be used in it's place.

=head2 L<WWW::LogicBoxes>

Primary interface to LogicBoxes.  Documented further below.

=head2 L<WWW::LogicBoxes::Contact>

WHOIS data contacts.  Typically (with few exceptions) domains contains a Registrant, Admin, Technical, and Billing contact.

=head2 L<WWW::LogicBoxes::Contact::US>

Extended contact used for .us domain registrations that contains the required L<Nexus Data|http://www.neustar.us/the-ustld-nexus-requirements/>.

=head2 L<WWW::LogicBoxes::Customer>

A LogicBoxes customer under the reseller account.

=head2 L<WWW::LogicBoxes::Domain>

A registered domain and all of it's related information.

=head2 L<WWW::LogicBoxes::DomainTransfer>

A pending domain transfer and all of it's related information.

=head2 L<WWW::LogicBoxes::DomainAvailability>

A response to a domain availability request.  Contains the status of the domain and if it is available for registration.

=head2 L<WWW::LogicBoxes::DomainRequest::Registration>

Request to register a domain.

=head2 L<WWW::LogicBoxes::DomainRequest::Transfer>

Request to transfer a domain.

=head2 L<WWW::LogicBoxes::PrivateNameServer>

Private Name Server record for a domain.  Not all domains will have these.

=head1 WITH

L<WWW::LogicBoxes::Role::Command>

=head1 ATTRIBUTES

=head2 B<username>

The reseller id to use.

=head2 password

B<NOTE> Password based authentication is now deprecated and is not allowed unless specifically requested from LogicBoxes for your reseller account.  Instead, you should be using the api_key.

=head2 api_key

The API Key used for authentication.  Either the password or the api_key B<MUST> be specified, but B<NOT> both.  For backwards compatability B<apikey> is an alias.

=head2 sandbox

Defaults to false.  Determines if requests should go to the production system L<https://httpapi.com> or the development environment L<https://test.httpapi.com>

=head2 response_type

Defaults to "xml."  Valid values include:

=over 4

=item xml

=item json

=item xml_simple

=back

It should be noted that this setting is really only relevant when making L<Raw|WWW::LogicBoxes::Role::Command::Raw> requests of the LogicBoxes API.  When doing so this attribute defines the format of the responses.

Defaults to

=head1 METHODS

=head2 new

    my $logic_boxes = WWW::LogicBoxes->new(
        username      => 'resellerid',

        # You may specify a password OR an api_key
        password      => 'Top S3cr3t!',
        api_key       => 'reseller_api_key',

        response_type => 'json',
        sandbox       => 0,
    );

Creates a new instance of WWW::LogicBoxes that can be used for API Requests.

=head1 AUTHORS

Robert Stone, C<< <drzigman AT cpan DOT org > >>

=head1 ACKNOWLEDGMENTS

Thanks to L<HostGator|http://hostgator.com> and L<BrainStorm Incubator|http://brainstormincubator.com> for funding the development of this module and providing test resources.

=head1 CONTRIBUTIONS

Special thanks to the following individuals who have offered commits, bug reports, and/or pull requests.

=over 4

=item Doug Schrag

=item Brandon Husbands

=item Slaven Rezic

=back

=head1 COPYRIGHT & LICENSE

Copyright 2016 Robert Stone

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU Lesser General Public License as published by the Free Software Foundation; or any compatible license.

See http://dev.perl.org/licenses/ for more information.
