package Net::OpenVPN::Manager::Plugin::PacketFilter;

use namespace::autoclean;
use AnyEvent::IO;
use Moose;
use MooseX::Types::Moose qw(Str Int ArrayRef HashRef);
use MooseX::Types::Structured qw(Dict Optional);
use Net::OpenVPN::Manager::Plugin;
use JSON qw(encode_json);
use Scalar::Util qw(reftype);

with 'Net::OpenVPN::Manager::Startable';
with 'Net::OpenVPN::Manager::Connectable';

has 'profile_attrs' => (
    is => 'ro',
    isa => 'ArrayRef[Str]',
    traits => ['Array'],
    default => sub { [] },
    handles => {
        all_profile_attrs => 'elements',
    },
);

has 'default_profiles' => (
    is => 'ro',
    isa => 'ArrayRef[Str]',
    traits => ['Array'],
    default => sub { [] },
    handles => {
        all_default_profiles => 'elements',
    },
);

has 'profiles' => (
    is => 'ro',
    traits => ['Hash'],
    isa => HashRef[ArrayRef|Dict["desc" => Optional[Str], "entries" => ArrayRef, "hidden" => Optional[Int]]],
    default => sub { {} },
    handles => {
        get_profile => 'get',
        profile_names => 'keys',
        has_profile => 'exists',
    }
);

sub start {
    my $self = shift;

    $self->manager->add_dispatcher(sub {
        my $env = shift;

        '/api/packetfilter/...' => sub {
            '' => sub {
                $self->manager->middleware_apikey;
            },
            'GET + /profiles' => sub {
                [200, ['Content-type' => 'application/json'], [encode_json({ profiles => [$self->profile_names]})]]
            },
            'GET + /profile/*' => sub {
                my $name = $_[1];
                unless ($self->has_profile($name)) {
                    [404, ['Content-type' => 'text/plain'], ["profile does not exist"]]
                } else {
                    [200, ['Content-type' => 'application/json'], [encode_json({ name => $name, content => _profile_entries($self->get_profile($name)) })]]
                }
            },
        },
    });

    return PLUG_OK;
}

sub connect {
    my ($self, $client) = @_;

    foreach my $attr ($self->all_profile_attrs) {
        foreach my $profile (@{$client->get_attr($attr)}) {
            next unless $self->has_profile($profile);
            $self->log("added $profile", $client);
            map { $client->add_pf($_) } @{_profile_entries($self->get_profile($profile))};
        }
    }

    foreach my $profile ($self->all_default_profiles) {
        next unless $self->has_profile($profile);
        $self->log("added $profile", $client);
        map { $client->add_pf($_) } @{_profile_entries($self->get_profile($profile))};
    }

    return PLUG_OK;
}

sub _profile_entries {
    my $profile = shift;

    return reftype($profile) eq "HASH"  ? $profile->{entries} : $profile;
}

__PACKAGE__->meta->make_immutable;

1;

