# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

package Bugzilla::Extension::BugmailFilter::Filter;

use base qw(Bugzilla::Object);

use 5.10.1;
use strict;
use warnings;

use Bugzilla::Component;
use Bugzilla::Error;
use Bugzilla::Extension::BugmailFilter::Constants;
use Bugzilla::Extension::BugmailFilter::FakeField;
use Bugzilla::Field;
use Bugzilla::Product;
use Bugzilla::User;
use Bugzilla::Util qw(trim);

use constant DB_TABLE => 'bugmail_filters';

use constant DB_COLUMNS => qw(
  id
  user_id
  product_id
  component_id
  field_name
  relationship
  changer_id
  action
);

use constant LIST_ORDER => 'id';

use constant UPDATE_COLUMNS => ();

use constant VALIDATORS => {
  user_id    => \&_check_user,
  field_name => \&_check_field_name,
  action     => \&Bugzilla::Object::check_boolean,
};
use constant VALIDATOR_DEPENDENCIES => {component_id => ['product_id'],};

use constant AUDIT_CREATES => 0;
use constant AUDIT_UPDATES => 0;
use constant AUDIT_REMOVES => 0;
use constant USE_MEMCACHED => 0;

# getters

sub user {
  my ($self) = @_;
  return Bugzilla::User->new({id => $self->{user_id}, cache => 1});
}

sub product {
  my ($self) = @_;
  return $self->{product_id}
    ? Bugzilla::Product->new({id => $self->{product_id}, cache => 1})
    : undef;
}

sub product_name {
  my ($self) = @_;
  return $self->{product_name}
    //= $self->{product_id} ? $self->product->name : '';
}

sub component {
  my ($self) = @_;
  return $self->{component_id}
    ? Bugzilla::Component->new({id => $self->{component_id}, cache => 1})
    : undef;
}

sub component_name {
  my ($self) = @_;
  return $self->{component_name}
    //= $self->{component_id} ? $self->component->name : '';
}

sub field_name {
  return $_[0]->{field_name} //= '';
}

sub field_description {
  my ($self, $value) = @_;
  $self->{field_description} = $value if defined($value);
  return $self->{field_description};
}

sub field {
  my ($self) = @_;
  return unless $self->{field_name};
  if (!$self->{field}) {
    if (substr($self->{field_name}, 0, 1) eq '~') {

      # this should never happen
      die "not implemented";
    }
    foreach my $field (
      @{Bugzilla::Extension::BugmailFilter::FakeField->fake_fields()},
      @{Bugzilla::Extension::BugmailFilter::FakeField->tracking_flag_fields()},
      )
    {
      if ($field->{name} eq $self->{field_name}) {
        return $self->{field} = $field;
      }
    }
    $self->{field}
      = Bugzilla::Field->new({name => $self->{field_name}, cache => 1});
  }
  return $self->{field};
}

sub relationship {
  return $_[0]->{relationship};
}

sub changer_id {
  return $_[0]->{changer_id};
}

sub changer {
  my ($self) = @_;
  return $self->{changer_id}
    ? Bugzilla::User->new({id => $self->{changer_id}, cache => 1})
    : undef;
}

sub relationship_name {
  my ($self) = @_;
  foreach my $rel (@{FILTER_RELATIONSHIPS()}) {
    return $rel->{name} if $rel->{value} == $self->{relationship};
  }
  return '?';
}

sub is_exclude {
  return $_[0]->{action} == 1;
}

sub is_include {
  return $_[0]->{action} == 0;
}

# validators

sub _check_user {
  my ($class, $user) = @_;
  $user || ThrowCodeError('param_required', {param => 'user'});
}

sub _check_field_name {
  my ($class, $field_name) = @_;
  return undef unless $field_name;
  if (substr($field_name, 0, 1) eq '~') {
    $field_name = lc(trim($field_name));
    $field_name =~ /^~[a-z0-9_\.\-]+$/ || ThrowUserError('bugmail_filter_invalid');
    length($field_name) <= 64 || ThrowUserError('bugmail_filter_too_long');
    return $field_name;
  }
  foreach my $rh (@{FAKE_FIELD_NAMES()}) {
    return $field_name if $rh->{name} eq $field_name;
  }
  return $field_name if $field_name =~ /^tracking\./;
  Bugzilla::Field->check({name => $field_name, cache => 1});
  return $field_name;
}

# methods

sub matches {
  my ($self, $args) = @_;

  if (my $field_name = $self->{field_name}) {
    if ($args->{field}->{field_name} && substr($field_name, 0, 1) eq '~') {
      my $substring = quotemeta(substr($field_name, 1));
      if ($args->{field}->{filter_field} !~ /$substring/i) {
        return 0;
      }
    }
    elsif ($field_name eq 'flagtypes.name') {
      if ($args->{field}->{field_name} ne $field_name) {
        return 0;
      }
    }
    elsif ($field_name ne $args->{field}->{filter_field}) {
      return 0;
    }
  }

  if ($self->{product_id} && $self->{product_id} != $args->{product_id}) {
    return 0;
  }

  if ($self->{component_id} && $self->{component_id} != $args->{component_id}) {
    return 0;
  }

  if ($self->{relationship} && !$args->{rel_map}->[$self->{relationship}]) {
    return 0;
  }

  if ($self->{changer_id} && $self->{changer_id} != $args->{changer_id}) {
    return 0;
  }

  return 1;
}

1;
