package App::Netdisco::DB::ResultSet::NodeIp;
use base 'App::Netdisco::DB::ResultSet';

use strict;
use warnings;

__PACKAGE__->load_components(qw/
  +App::Netdisco::DB::ExplicitLocking
/);

my $search_attr = {
    order_by => {'-desc' => 'time_last'},
    '+columns' => [
      'oui.company',
      'oui.abbrev',
      { time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')" },
      { time_last_stamp =>  \"to_char(time_last, 'YYYY-MM-DD HH24:MI')" },
    ],
    join => 'oui'
};

=head1 with_times

This is a modifier for any C<search()> (including the helpers below) which
will add the following additional synthesized columns to the result set:

=over 4

=item time_first_stamp

=item time_last_stamp

=back

=cut

sub with_times {
  my ($rs, $cond, $attrs) = @_;

  return $rs
    ->search_rs({}, $search_attr)
    ->search($cond, $attrs);
}

=head1 search_by_ip( \%cond, \%attrs? )

 my $set = $rs->search_by_ip({ip => '192.0.2.1', active => 1});

Like C<search()>, this returns a ResultSet of matching rows from the
NodeIp table.

=over 4

=item *

The C<cond> parameter must be a hashref containing a key C<ip> with the value
to search for. Value can either be a simple string of IPv4 or IPv6, or a
L<NetAddr::IP::Lite> object in which case all results within the CIDR/Prefix
will be retrieved.

=item *

Results are ordered by time last seen.

=item *

Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.

=item *

A JOIN is performed on the OUI table and the OUI C<company> column prefetched.

=back

To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.

=cut

sub search_by_ip {
    my ($rs, $cond, $attrs) = @_;

    die "ip address required for search_by_ip\n"
      if ref {} ne ref $cond or !exists $cond->{ip};

    # handle either plain text IP or NetAddr::IP (/32 or CIDR)
    my ($op, $ip) = ('=', delete $cond->{ip});

    if ('NetAddr::IP::Lite' eq ref $ip and $ip->num > 1) {
        $op = '<<=';
        $ip = $ip->cidr;
    }
    $cond->{ip} = { $op => $ip };

    return $rs
      ->search_rs({}, $search_attr)
      ->search($cond, $attrs);
}

=head1 search_by_dns( \%cond, \%attrs? )

 my $set = $rs->search_by_dns({
   dns => 'foo.example.com',
   suffix => qr/(?:\.example\..com|\.local)$/,
   active => 1
 });

Like C<search()>, this returns a ResultSet of matching rows from the
NodeIp table.

=over 4

=item *

The NodeIp table must have a C<dns> column for this search to work. Typically
this column is the IP's DNS PTR record, cached at the time of Netdisco Arpnip.

=item *

The C<cond> parameter must be a hashref containing a key C<dns> with the value
to search for. The value may optionally include SQL wildcard characters.

=item *

The C<cond> parameter may optionally have a C<suffix> parameter which is a
regular expression of domain names - one of which must match the results.

=item *

Results are ordered by time last seen.

=item *

Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.

=item *

A JOIN is performed on the OUI table and the OUI C<company> column prefetched.

=back

To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.

=cut

sub search_by_dns {
    my ($rs, $cond, $attrs) = @_;

    die "dns field required for search_by_dns\n"
      if ref {} ne ref $cond or !exists $cond->{dns};

    (my $suffix = (delete $cond->{suffix} || ''))
      =~ s|\Q(?^\Eu?|(?|g;

    $cond->{dns} = [ -and =>
      { '-ilike' => delete $cond->{dns} },
      { '~*' => "***:$suffix" },
    ];

    return $rs
      ->search_rs({}, $search_attr)
      ->search($cond, $attrs);
}

=head1 search_by_mac( \%cond, \%attrs? )

 my $set = $rs->search_by_mac({mac => '00:11:22:33:44:55', active => 1});

Like C<search()>, this returns a ResultSet of matching rows from the
NodeIp table.

=over 4

=item *

The C<cond> parameter must be a hashref containing a key C<mac> with the value
to search for.

=item *

Results are ordered by time last seen.

=item *

Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.

=item *

A JOIN is performed on the OUI table and the OUI C<company> column prefetched.

=back

To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.

=cut

sub search_by_mac {
    my ($rs, $cond, $attrs) = @_;

    die "mac address required for search_by_mac\n"
      if ref {} ne ref $cond or !exists $cond->{mac};

    return $rs
      ->search_rs({}, $search_attr)
      ->search($cond, $attrs);
}

=head2 ip_version( $version )

 my $rset = $rs->ip_version(4);

This predefined C<search()> returns a ResultSet of matching rows from the
NodeIp table of nodes with addresses of the supplied IP version.

=over 4

=item *

The C<version> parameter must be an integer either 4 or 6.

=back

=cut

sub ip_version {
    my ( $rs, $version ) = @_;

    die "ip_version input must be either 4 or 6\n"
        unless $version && ( $version == 4 || $version == 6 );

    return $rs->search_rs( \[ 'family(me.ip) = ?', $version ] );
}

1;
