The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package App::Netdisco::DB::ResultSet::Node;
use base 'App::Netdisco::DB::ResultSet';

use strict;
use warnings FATAL => 'all';

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

=head1 ADDITIONAL METHODS

=head2 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 Node
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 Device table and the Device C<dns> column
prefetched.

=back

To limit results only to active nodes, 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};

    $cond->{'me.mac'} = delete $cond->{mac};

    return $rs
      ->search_rs({}, {
        order_by => {'-desc' => 'time_last'},
        '+columns' => [
          'device.dns',
          { 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 => 'device',
      })
      ->search($cond, $attrs);
}

=head1 SPECIAL METHODS

=head2 delete( \%options? )

Overrides the built-in L<DBIx::Class> delete method to more efficiently
handle the removal or archiving of nodes.

=cut

sub delete {
  my $self = shift;
  my ($opts) = @_;
  $opts = {} if (ref {} ne ref $opts);

  my $schema = $self->result_source->schema;
  my $nodes = $self->search(undef, { columns => 'mac' });

  if (exists $opts->{archive_nodes} and $opts->{archive_nodes}) {
      foreach my $set (qw/
        NodeIp
        NodeNbt
        NodeMonitor
        Node
      /) {
          $schema->resultset($set)->search(
            { mac => { '-in' => $nodes->as_query }},
          )->update({ active => \'false' });
      }

      $schema->resultset('NodeWireless')
        ->search({ mac => { '-in' => $nodes->as_query }})->delete;

      # avoid letting DBIC delete nodes
      return 0E0;
  }
  elsif (exists $opts->{only_nodes} and $opts->{only_nodes}) {
      # now let DBIC do its thing
      return $self->next::method();
  }
  elsif (exists $opts->{keep_nodes} and $opts->{keep_nodes}) {
      # avoid letting DBIC delete nodes
      return 0E0;
  }
  else {
      foreach my $set (qw/
        NodeIp
        NodeNbt
        NodeMonitor
        NodeWireless
      /) {
          $schema->resultset($set)->search(
            { mac => { '-in' => $nodes->as_query }},
          )->delete;
      }

      # now let DBIC do its thing
      return $self->next::method();
  }
}

=head2 with_multi_ips_as_hashref

This is a modifier for C<search()> which returns a list of hash references
for nodes within the search criteria with multiple IP addresses.  Each hash
reference contains the keys:

=over 4

=item mac

Node MAC address.

=item switch

IP address of the device where the node is attached.

=item port

Port on the device where the node is attached.

=item dns

DNS name of the device where the node is attached.

=item name

C<sysName> of the device where the node is attached.

=item ip_count

Count of IP addresses associated with the node.

=item vendor

Vendor string based upon the node OUI.

=back

=cut

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

  my @return = $rs->search(
    {},
    { result_class => 'DBIx::Class::ResultClass::HashRefInflator',
      select       => [ 'mac', 'switch', 'port' ],
      join         => [qw/device ips oui/],
      '+columns'   => [
        { 'dns'      => 'device.dns' },
        { 'name'     => 'device.name' },
        { 'ip_count' => { count => 'ips.ip' } },
        { 'vendor'   => 'oui.company' }
      ],
      group_by =>
        [qw/ me.mac me.switch me.port device.dns device.name oui.company/],
      having => \[ 'count(ips.ip) > ?', [ count => 1 ] ],
      order_by => { -desc => [qw/count/] },
    }
  )->all;

  return \@return;
}

1;