package Weather::OpenWeatherMap::Request;
$Weather::OpenWeatherMap::Request::VERSION = '0.003001';
use feature 'state';
use strictures 2;
use Carp;

use Types::Standard -all;
use Types::DateTime -all;

use HTTP::Request;
use URI::Escape 'uri_escape_utf8';

use Module::Runtime 'use_module';


use Moo; 

use Storable 'freeze';

sub new_for {
  my ($class, $type) = splice @_, 0, 2;
  confess "Expected a subclass type" unless $type;
  my $subclass = join '::', $class, map ucfirst, split '::', $type;
  use_module($subclass)->new(@_)
}

=pod

=for Pod::Coverage has_\w+

=cut

has api_key => (
  lazy      => 1,
  is        => 'ro',
  isa       => Str,
  predicate => 1,
  builder   => sub { '' },
);


has tag => (
  lazy      => 1,
  is        => 'ro',
  predicate => 1,
  builder   => sub { '' },
);

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


has ts => (
  is        => 'ro',
  isa       => StrictNum,
  builder   => sub { time },
);

has url => (
  init_arg  => undef,
  lazy      => 1,
  is        => 'ro',
  isa       => Str,
  builder   => sub { shift->_parse_location_str },
);

has http_request => (
  lazy      => 1,
  is        => 'ro',
  isa       => InstanceOf['HTTP::Request'],
  builder   => sub {
    my ($self) = @_;
    my $req = HTTP::Request->new( GET => $self->url );
    $req->header( 'x-api-key' => $self->api_key )
      if $self->has_api_key and length $self->api_key;
    $req
  },
);


has _units => (
  required => 1,
  is       => 'ro',
  isa      => Str,
  builder  => sub { 'imperial' },
);



sub _url_bycode {
  my ($self, $code) = @_;
  'http://api.openweathermap.org/data/2.5/weather?id='
    . uri_escape_utf8($code)
    . '&units=' . $self->_units
}

sub _url_bycoord {
  my $self = shift;
  my ($lat, $long) = map {; uri_escape_utf8($_) } @_;
  "http://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$long"
    . '&units=' . $self->_units
}

sub _url_byname {
  my ($self, @parts) = @_;
  'http://api.openweathermap.org/data/2.5/weather?q='
    . join(',', map {; uri_escape_utf8($_) } @parts)
    . '&units=' . $self->_units
}


sub _parse_location_str {
  my ($self) = @_;

  state $latlong =
    qr{\Alat(?:itude)?\s+?(-?[0-9.]+),?\s+?long?(?:itude)?\s+?(-?[0-9.]+)};

  my $str = $self->location;
  my $url;
  URL: {
    if (is_StrictNum($str)) {
      $url = $self->_url_bycode($str);
      last URL
    }

    if (my ($lat, $lon) = $str =~ $latlong) {
      $url = $self->_url_bycoord($lat, $lon);
      last URL
    }

    my @parts = split /,\s+?/, $str;
    $url = $self->_url_byname(@parts);
  }

  $url
}

1;

=pod

=head1 NAME

Weather::OpenWeatherMap::Request - Weather lookup request superclass

=head1 SYNOPSIS

 # Normally generated via Weather::OpenWeatherMap
 # (when a request is made)

=head1 DESCRIPTION

This is the parent class for request objects generated by 
L<Weather::OpenWeatherMap>.

The L</"SEE ALSO"> section links known subclasses.

=head2 ATTRIBUTES

=head3 api_key

The L<OpenWeatherMap API key|http://www.weathermap.org/api> attached to this
request.

=head3 location

B<Required:>

The location string, used to (lazily) generate the L</http_request> and L</url>
attributes.

Locations can be specified in various ways.

Strings such as 'City, State' or 'City, Country' will be parsed appropriately.

A numeric location is taken to be an
L<OpenWeatherMap|http://www.openweathermap.org> city code.

A string in the form of 'lat X, long Y' or 'latitude X, longitude Y' is also
accepted.

=head3 tag

An (optional) arbitrary scalar attached to the request object at construction
time.

=head3 http_request

The (generated) L<HTTP::Request> object used to request the weather data.

=head3 ts

The C<time()> the request object was created.

=head3 url

The (generated) L<OpenWeatherMap|http://www.openweathermap.org/> URL.

=head2 METHODS

=head3 new_for

Factory method; returns a new object belonging to the appropriate subclass:

  my $request = Weather::OpenWeatherMap::Request->new_for(
    Current =>
      api_key  => $api_key,
      location => $location,
      tag      => $tag,
  );

=head1 SEE ALSO

L<Weather::OpenWeatherMap::Request::Current>

L<Weather::OpenWeatherMap::Request::Forecast>

L<Weather::OpenWeatherMap::Request::Find>

L<Weather::OpenWeatherMap::Result>

=head1 AUTHOR

Jon Portnoy <avenj@cobaltirc.org>

=cut

