package NGCP::Panel::Role::API::Subscribers;
use NGCP::Panel::Utils::Generic qw(:all);

use Sipwise::Base;

use parent 'NGCP::Panel::Role::API';

use boolean qw(true);
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use JSON::Types;
use Test::More;
use POSIX qw(ceil);
use NGCP::Panel::Form;
use NGCP::Panel::Utils::XMLDispatcher;
use NGCP::Panel::Utils::Subscriber;
use NGCP::Panel::Utils::Events;
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Contract qw();
use NGCP::Panel::Utils::Encryption qw();
use NGCP::Panel::Utils::Auth qw();

sub resource_name{
    return 'subscribers';
}

sub dispatch_path{
    return '/api/subscribers/';
}

sub relation{
    return 'http://purl.org/sipwise/ngcp-api/#rel-subscribers';
}

sub get_form {
    my ($self, $c) = @_;

    if ($c->user->roles eq "admin" || $c->user->roles eq "reseller" ||
        $c->user->roles eq "ccareadmin" || $c->user->roles eq "ccare") {
        return (NGCP::Panel::Form::get("NGCP::Panel::Form::Subscriber::SubscriberAPI", $c));
    } elsif ($c->user->roles eq "subscriberadmin" || $c->user->roles eq "subscriber") {
        return (NGCP::Panel::Form::get("NGCP::Panel::Form::Subscriber::SubscriberSubAdminAPI", $c));
    }
}

sub resource_from_item {
    my ($self, $c, $item, $form, $patch_mode) = @_;
    my $pref;

    my $bill_resource = { $item->get_inflated_columns };
    my $prov_resource = { $item->provisioning_voip_subscriber->get_inflated_columns };
    my $customer = $self->get_customer($c, $item->contract_id);
    delete $prov_resource->{domain_id};
    delete $prov_resource->{account_id};
    my %resource = %{ merge($bill_resource, $prov_resource) };
    $resource{administrative} = delete $resource{admin};

    unless($customer->product->class eq 'pbxaccount') {
        delete $resource{is_pbx_group};
        delete $resource{is_pbx_pilot};
        delete $resource{pbx_extension};
    }
    unless(is_true($resource{is_pbx_group})) {
        delete $resource{pbx_hunt_policy};
        delete $resource{cloud_pbx_hunt_policy};
        delete $resource{pbx_hunt_timeout};
        delete $resource{cloud_pbx_hunt_timeout};
        delete $resource{pbx_hunt_cancel_mode};
        delete $resource{cloud_pbx_hunt_cancel_mode};
    }
    delete $resource{contact_id};
    if($item->contact) {
        $resource{email} = $item->contact->email;
        $resource{timezone} = $item->contact->timezone;
    } else {
        $resource{email} = undef;
        $resource{timezone} = undef;
    }
    my $sippassword = $resource{password};
    my $webpassword = $resource{webpassword};

    if(!$form){
        ($form) = $self->get_form($c);
    }

    # form validation during PATCH causes
    # fields to be removed from the %resource
    # and then apply_patch() removes the fields
    # that were not a part the PATCH ops from
    # the database, therefore a copy of the resource
    # is validated instead, preserving the original one
    # when $patch_mode is enabled
    my %validate_resource = %resource;
    last unless $self->validate_form(
        c => $c,
        resource => $patch_mode ? \%validate_resource : \%resource,
        form => $form,
        run => 0,
    );

    $resource{_password} = $sippassword;
    $resource{_webpassword} = $webpassword;

    if($customer->product->class eq 'pbxaccount') {
        if ($resource{administrative} == 1) {
            $resource{ext_range_min} = $customer->voip_contract_preferences->search(
                {
                    'attribute.attribute' => 'ext_range_min'
                },
                {
                    join => 'attribute',
                }
            )->get_column('value')->first;

            $resource{ext_range_max} = $customer->voip_contract_preferences->search(
                {
                    'attribute.attribute' => 'ext_range_max'
                },
                {
                    join => 'attribute',
                }
            )->get_column('value')->first;
        }
        $resource{pbx_group_ids} = [];
        foreach my $group($item->provisioning_voip_subscriber->voip_pbx_groups->search_rs(undef,{'order_by' => 'me.id'})->all) {
            push @{ $resource{pbx_group_ids} }, int($group->group->voip_subscriber->id);
        }
        if($item->provisioning_voip_subscriber->is_pbx_group) {
            $resource{pbx_groupmember_ids} = [];
            foreach my $member($item->provisioning_voip_subscriber->voip_pbx_group_members->search_rs(undef,{'order_by' => 'me.id'})->all) {
                push @{ $resource{pbx_groupmember_ids} }, int($member->subscriber->voip_subscriber->id);
            }
        }
    }

    if($item->primary_number) {
        $resource{primary_number}->{cc} = $item->primary_number->cc;
        $resource{primary_number}->{ac} = $item->primary_number->ac;
        $resource{primary_number}->{sn} = $item->primary_number->sn;
        $resource{primary_number}->{number_id} = int($item->primary_number->id);
    }

    my @aliases = ();
    if($item->voip_numbers->first) {
        foreach my $n ($item->voip_numbers->search(undef,{
                prefetch => 'voip_dbalias',
            })->all) {
            my $alias = {
                cc => $n->cc,
                ac => $n->ac,
                sn => $n->sn,
                number_id => int($n->id),
            };
            next if ($resource{primary_number} && $resource{primary_number}->{number_id} == $alias->{number_id});
            $alias->{is_devid} = 0;
            if (defined $n->voip_dbalias) {
                $alias->{is_devid} = $n->voip_dbalias->is_devid;
            }
            $alias->{is_devid} = bool $alias->{is_devid};
            push(@aliases, $alias);
        }
    }
    $resource{alias_numbers} = \@aliases;

    $pref = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
        c => $c, attribute => 'display_name',
        prov_subscriber => $item->provisioning_voip_subscriber);
    if($pref->first && $pref->first->value) {
        $resource{display_name} = $pref->first->value;
    } else {
        $resource{display_name} = undef;
    }

    $resource{id} = int($item->id);
    $resource{domain} = $item->domain->domain;

    # don't leak internal info to subscribers via API for those fields
    # not filtered via forms
    my $contract_id = int(delete $resource{contract_id});
    if ($c->user->roles eq "admin" || $c->user->roles eq "reseller" ||
        $c->user->roles eq "ccareadmin" || $c->user->roles eq "ccare") {
        $resource{customer_id} = $contract_id;
        $resource{uuid} = $item->uuid;

        my $pref = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
            c => $c, attribute => 'lock',
            prov_subscriber => $item->provisioning_voip_subscriber);
        $resource{lock} = 0;
        if($pref->first and length($pref->first->value) > 0) {
            #cast to Numeric accordingly to the form field type and customer note in the ticket #10313
            $resource{lock} = $pref->first->value;
        }else{
            $resource{lock} = undef;
        }
        if ($c->user->show_passwords) {
            foreach my $k(qw/password webpassword/) {
                eval {
                    if (not NGCP::Panel::Utils::Auth::is_salted_hash($resource{$k})) {
                        $resource{'_' . $k} = NGCP::Panel::Utils::Encryption::encrypt_rsa($c,$resource{$k});
                    } else {
                        delete $resource{'_' . $k};
                    }
                };
                if ($@) {
                    $c->error("Failed to encrypt $k: " . $@);
                    delete $resource{'_' . $k};
                }
            }
        } else {
            foreach my $k(qw/password webpassword/) {
                delete $resource{'_' . $k};
            }
        }
    } else {
        delete $resource{'password'} if $c->user->roles eq 'subscriber';

        if ($c->user->roles eq "subscriberadmin") {
            $resource{customer_id} = $contract_id;
            if ($item->id != $c->user->voip_subscriber->id) {
                if (!$c->config->{security}->{password}->{sip_expose_subadmin}) {
                    delete $resource{_password};
                }
                if (!$c->config->{security}->{password}->{web_expose_subadmin}) {
                    delete $resource{_webpassword};
                }
            }
        }
    }

    return \%resource;
}

sub hal_from_item {
    my ($self, $c, $item, $resource, $form) = @_;
    my $is_sub = 1;
    if ($c->user->roles eq "admin" || $c->user->roles eq "reseller" ||
        $c->user->roles eq "ccareadmin" || $c->user->roles eq "ccare") {
        $is_sub = 0;
    }
    my $is_subadm = 1;
    if($c->user->roles eq "subscriber") {
        $is_subadm = 0;
    }

    delete $resource->{password};
    delete $resource->{webpassword};
    $resource->{password} = delete $resource->{_password} if exists $resource->{_password};
    $resource->{webpassword} = delete $resource->{_webpassword} if exists $resource->{_webpassword};

    my $hal = Data::HAL->new(
        links => [
            Data::HAL::Link->new(
                relation => 'curies',
                href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}',
                name => 'ngcp',
                templated => true,
            ),
            Data::HAL::Link->new(relation => 'collection', href => sprintf("/api/%s/", $self->resource_name)),
            Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
            Data::HAL::Link->new(relation => 'self', href => sprintf("%s%d", $self->dispatch_path, $item->id)),

            # available also to subscribers
            Data::HAL::Link->new(relation => 'ngcp:subscriberpreferences', href => sprintf("/api/subscriberpreferences/%d", $item->id)),
            Data::HAL::Link->new(relation => 'ngcp:voicemailsettings', href => sprintf("/api/voicemailsettings/%d", $item->id)),
            Data::HAL::Link->new(relation => 'ngcp:reminders', href => sprintf("/api/reminders/?subscriber_id=%d", $item->id)),
            Data::HAL::Link->new(relation => 'ngcp:callforwards', href => sprintf("/api/callforwards/%d", $item->id)),

            # only available to admins/resellers
            ($is_sub ? () : (
                ($item->provisioning_voip_subscriber && $item->provisioning_voip_subscriber->profile_set_id) ? (Data::HAL::Link->new(relation => 'ngcp:subscriberprofilesets', href => sprintf("/api/subscriberprofilesets/%d", $item->provisioning_voip_subscriber->profile_set_id))) : (),
                ($item->provisioning_voip_subscriber && $item->provisioning_voip_subscriber->profile_id) ? (Data::HAL::Link->new(relation => 'ngcp:subscriberprofiles', href => sprintf("/api/subscriberprofiles/%d", $item->provisioning_voip_subscriber->profile_id))) : (),
                Data::HAL::Link->new(relation => 'ngcp:domains', href => sprintf("/api/domains/%d", $item->domain->id)),
                Data::HAL::Link->new(relation => 'ngcp:calls', href => sprintf("/api/calls/?subscriber_id=%d", $item->id)),
                Data::HAL::Link->new(relation => 'ngcp:subscriberregistrations', href => sprintf("/api/subscriberregistrations/?subscriber_id=%d", $item->id)),
                #Data::HAL::Link->new(relation => 'ngcp:trustedsources', href => sprintf("/api/trustedsources/%d", $item->contract->id)),
                $self->get_journal_relation_link($c, $item->id),
            )),
            # only available to admins/resellers/subscriberadmins
            (!$is_subadm ? () : (
                Data::HAL::Link->new(relation => 'ngcp:customers', href => sprintf("/api/customers/%d", $item->contract_id)),
            )),
        ],
        relation => 'ngcp:'.$self->resource_name,
    );

    $self->expand_fields($c, $resource);
    $hal->resource($resource);
    return $hal;
}

sub _item_rs {
    my ($self, $c) = @_;

    my $item_rs;
    $item_rs = $c->model('DB')->resultset('voip_subscribers')
        ->search({ 'me.status' => { '!=' => 'terminated' } });
    if ($c->user->roles eq "admin" || $c->user->roles eq "ccareadmin") {
        $item_rs = $item_rs->search(undef,
        {
            join => { 'contract' => 'contact' }, #for filters
        });
    } elsif ($c->user->roles eq "reseller" || $c->user->roles eq "ccare") {
        $item_rs = $item_rs->search({
            'contact.reseller_id' => $c->user->reseller_id,
        }, {
            join => { 'contract' => 'contact' },
        });
    } elsif ($c->user->roles eq "subscriberadmin") {
        $item_rs = $item_rs->search({
            'contract_id' => $c->user->account_id,
        });
    } elsif ($c->user->roles eq "subscriber") {
        $item_rs = $item_rs->search({
            #voip_subscriber is a provisioning.voip_subscribers relation
            #$c->user is provisioning.voip_subscribers, so we use ->voip_subscriber->id and compare to billing.voip-subscribers.
            'me.id' => $c->user->voip_subscriber->id,
        });
    } else {
        $self->error($c, HTTP_FORBIDDEN, "Invalid authentication role");
        return;
    }

    return $item_rs;
}

sub item_by_id {
    my ($self, $c, $id) = @_;

    my $item_rs = $self->item_rs($c);
    return $item_rs->find($id);
}

sub get_customer {
    my ($self, $c, $customer_id) = @_;

    my $customer_rs = NGCP::Panel::Utils::Contract::get_contract_rs(
        schema => $c->model('DB'),
        contract_id => $customer_id,
    );
    $customer_rs = $customer_rs->search({
            'contact.reseller_id' => { '-not' => undef },
            'me.id' => $customer_id,
        },{
            join => 'contact',
        });
    my @product_ids = map { $_->id; } $c->model('DB')->resultset('products')->search_rs({ 'class' => ['sipaccount','pbxaccount'] })->all;
    $customer_rs = $customer_rs->search({
        'product_id' => { -in => [ @product_ids ] },
    });
    if ($c->user->roles eq "admin" || $c->user->roles eq "ccareadmin") {
    } elsif($c->user->roles eq "reseller" || $c->user->roles eq "ccare") {
        $customer_rs = $customer_rs->search({
            'contact.reseller_id' => $c->user->reseller_id,
        });
    }
    my $customer = $customer_rs->first;
    unless($customer) {
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'customer_id', doesn't exist.");
        return;
    }
    return $customer;
}

sub prepare_resource {
    my ($self, $c, $schema, $resource, $item, $patch_mode) = @_;

    return NGCP::Panel::Utils::Subscriber::prepare_resource(
        c => $c,
        schema => $c->model('DB'),
        resource => $resource,
        item => $item,
        err_code => sub {
            my ($code, $msg, @errors) = @_;
            $self->error($c, $code, $msg, @errors);
        },
        validate_code => sub {
            my ($r) = @_;
            my ($form) = $self->get_form($c);
            # form validation during PATCH causes
            # fields to be removed from the %resource
            # and then apply_patch() removes the fields
            # that were not a part the PATCH ops from
            # the database, therefore a copy of the resource
            # is validated instead, preserving the original one
            # when $patch_mode is enabled
            my %validate_resource = %{$r};
            return $self->validate_form(
                c => $c,
                resource => $patch_mode ? \%validate_resource : $r,
                form => $form,
            );
        },
        getcustomer_code => sub {
            my ($cid) = @_;
            my $contract = $self->get_customer($c, $cid);

            if ($c->config->{api}{underrun_lock_on_request}) {
                NGCP::Panel::Utils::Contract::acquire_contract_rowlocks(
                    c => $c,
                    schema => $c->model('DB'),
                    contract_id => $contract->id,
                    skip_locked => ($c->request->header('X-Delay-Commit') ? 0 : 1),
                ) if $contract;
            }

            return $contract;
        },
    );

}

sub process_form_resource {
    my($self,$c, $item, $old_resource, $resource, $form, $process_extras) = @_;

    $resource->{timezone} = NGCP::Panel::Utils::DateTime::get_timezone_link($c, $resource->{timezone});
}

sub update_item {
    my ($self, $c, $schema, $item, $full_resource, $resource, $form) = @_;

    return unless $self->check_write_access($c, $item->id);

    my $subscriber = $item;
    my $customer = $full_resource->{customer};
    my $alias_numbers = $full_resource->{alias_numbers};
    my $preferences = $full_resource->{preferences};
    my $groups = $full_resource->{groups};
    my $groupmembers = $full_resource->{groupmembers};
    my $prov_subscriber = $subscriber->provisioning_voip_subscriber;

    $self->process_form_resource($c, $item, $full_resource, $resource, $form);

    if($subscriber->provisioning_voip_subscriber->is_pbx_pilot && !is_true($resource->{is_pbx_pilot})) {
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Cannot revoke is_pbx_pilot status from a subscriber.");
        return;
    }

    if($resource->{customer_id} && ( $resource->{customer_id} != $subscriber->contract->id ) ){
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "customer_id can't be changed.");
        return;
    }

    if ($resource->{timezone} && !NGCP::Panel::Utils::DateTime::is_valid_timezone_name($resource->{timezone})) {
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "invalid timezone name.");
        return;
    }

    if($subscriber->status ne $resource->{status}) {
        if($resource->{status} eq 'locked') {
            $resource->{lock} = 4;
        } elsif($subscriber->status eq 'locked' && $resource->{status} eq 'active') {
            $resource->{lock} ||= 0;
        } elsif($resource->{status} eq 'terminated') {
            try {
                NGCP::Panel::Utils::Subscriber::terminate(c => $c, subscriber => $subscriber);
                return $subscriber;
            } catch($e) {
                $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to terminate subscriber",
                             "failed to terminate subscriber id ".$subscriber->id, $e);
                return;
            }
        }
    }
    try {
        NGCP::Panel::Utils::Subscriber::lock_provisoning_voip_subscriber(
            c => $c,
            prov_subscriber => $prov_subscriber,
            level => $resource->{lock} || 0,
        );
    } catch($e) {
        $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update subscriber lock",
                     "failed to lock subscriber id ".$subscriber->id." with level ".$resource->{lock}, $e);
        return;
    };

    my ($error,$profile_set,$profile) = NGCP::Panel::Utils::Subscriber::check_profile_set_and_profile($c, $resource, $subscriber);
    if ($error) {
        $self->error($c, $error->{response_code}, $error->{description}, $error->{error});
        return;
    }

    if($resource->{email} || $resource->{timezone}) {
        my $contact = $subscriber->contact;
        unless ($contact) {
            $contact = $schema->resultset('contacts')->create({
                reseller_id => $subscriber->contract->contact->reseller_id,
            });
        }
        if(not $contact->email or ($contact->email ne $resource->{email})) {
            $contact->update({
                email => $resource->{email},
            });
        }
        if(not $contact->timezone or ($contact->timezone ne $resource->{timezone})) {
            $contact->update({
                timezone => $resource->{timezone},
            });
        }
        $resource->{contact_id} = $contact->id;
    } elsif($subscriber->contact) {
        try {
            $c->log->debug("delete contact id ".$subscriber->contact->id);
            $subscriber->contact->delete;
        } catch($e) {
            $c->log->debug("contact still in use: ".$e);
        }
        $resource->{contact_id} = undef; # mark for clearance
    }
    delete $resource->{email};
    delete $resource->{timezone};

    my $aliases_before = NGCP::Panel::Utils::Events::get_aliases_snapshot(
        c => $c,
        schema => $schema,
        subscriber => $subscriber,
    );

    try {
        NGCP::Panel::Utils::Subscriber::update_subscriber_numbers(
            c => $c,
            schema => $schema,
            primary_number => $resource->{e164},
            alias_numbers => $alias_numbers,
            reseller_id => $customer->contact->reseller_id,
            subscriber_id => $subscriber->id,
        );
    } catch(DBIx::Class::Exception $e where { /Duplicate entry '([^']+)' for key 'number_idx'/ }) {
        $e =~ /Duplicate entry '([^']+)' for key 'number_idx'/;
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number '" . $1 . "' already exists.", "Number already exists.",
                     "failed to update subscriber, number " . $c->qs($1) . " already exists");
        return;
    } catch ($e where { /alias \d+ already exists/ }) {
        $e =~ /alias (\d+) already exists/;
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number '" . $1 . "' already exists.", "Number already exists.",
                     "failed to update subscriber, alias " . $c->qs($1) . " already exists");
        return;
    } catch ($e where { /aliases \S+ already exist/ }) {
        $e =~ /aliases (\S+) already exist/;
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Numbers '" . $1 . "' already exist.", "Numbers already exist.",
                     "failed to update subscriber, aliases " . $c->qs($1) . " already exist");
        return;
    } catch ($e where { /more than \d+ provided aliases/ }) {
        my $err_msg = "more than 10 provided aliases already exist";
        $self->error($c, HTTP_UNPROCESSABLE_ENTITY, $err_msg);
        return;
    }

    my $billing_res = {
        external_id => $resource->{external_id},
        status => $resource->{status},
        contact_id => $resource->{contact_id},
    };

    if ($resource->{password} && $resource->{password} ne $prov_subscriber->password) {
        NGCP::Panel::Utils::Subscriber::insert_password_journal(
            $c, $prov_subscriber, $resource->{password}
        );
    }

    if ($resource->{webpassword}) {
        NGCP::Panel::Utils::Subscriber::insert_webpassword_journal(
            $c, $prov_subscriber, $resource->{webpassword}
        );
    }

    if (exists $resource->{webpassword} and $NGCP::Panel::Utils::Auth::ENCRYPT_SUBSCRIBER_WEBPASSWORDS) {
        $resource->{webpassword} = NGCP::Panel::Utils::Auth::generate_salted_hash($resource->{webpassword});
    }

    my $provisioning_res = {
        webusername => $resource->{webusername},
        is_pbx_pilot => $resource->{is_pbx_pilot} // 0,
        is_pbx_group => $resource->{is_pbx_group} // 0,
        modify_timestamp => NGCP::Panel::Utils::DateTime::current_local,
        profile_set_id => $profile_set ? $profile_set->id : undef,
        profile_id => $profile ? $profile->id : undef,
        pbx_extension => $resource->{pbx_extension},
        defined $resource->{administrative} ? (admin => $resource->{administrative}) : (),
    };
    $provisioning_res->{password} = $resource->{password} if exists $resource->{password};
    $provisioning_res->{webpassword} = $resource->{webpassword} if exists $resource->{webpassword};
    if(is_true($resource->{is_pbx_group})) {
        $provisioning_res->{pbx_hunt_policy} = $resource->{pbx_hunt_policy};
        $provisioning_res->{pbx_hunt_timeout} = $resource->{pbx_hunt_timeout};
        $provisioning_res->{pbx_hunt_cancel_mode} = $resource->{pbx_hunt_cancel_mode};
        NGCP::Panel::Utils::Subscriber::update_preferences(
            c => $c,
            prov_subscriber => $prov_subscriber,
            'preferences'   => {
                cloud_pbx_hunt_policy  => $resource->{cloud_pbx_hunt_policy} // $resource->{pbx_hunt_policy},
                cloud_pbx_hunt_timeout => $resource->{cloud_pbx_hunt_timeout} // $resource->{pbx_hunt_timeout},
                cloud_pbx_hunt_cancel_mode => $resource->{cloud_pbx_hunt_cancel_mode} // $resource->{pbx_hunt_cancel_mode},
            }
        );
    }
    my $old_profile = $prov_subscriber->profile_id;

    $subscriber->update($billing_res);
    $subscriber->discard_changes;
    $prov_subscriber->update($provisioning_res);
    $prov_subscriber->discard_changes;

    NGCP::Panel::Utils::Events::insert_profile_events(
        c => $c, schema => $schema, subscriber_id => $subscriber->id,
        old => $old_profile, new => $prov_subscriber->profile_id,
        %$aliases_before,
    );

    NGCP::Panel::Utils::Subscriber::update_preferences(
        c => $c,
        prov_subscriber => $prov_subscriber,
        preferences => $preferences,
    );

    NGCP::Panel::Utils::Subscriber::manage_pbx_groups(
        c            => $c,
        schema       => $schema,
        groups       => $groups,
        groupmembers => $groupmembers,
        customer     => $customer,
        subscriber   => $subscriber,
    );

    return $subscriber;
}

sub check_write_access {
    my ($self, $c, $id) = @_;

    if ($c->user->roles eq "admin" || $c->user->roles eq "reseller" ||
        $c->user->roles eq "ccareadmin" || $c->user->roles eq "ccare") {
            return 1;
    } elsif ($c->user->roles eq "subscriberadmin") {
        if (!$self->subscriberadmin_write_access($c, $id) && $id != $c->user->voip_subscriber->id) {
            $self->error($c, HTTP_FORBIDDEN, "Read-only resource for authenticated role");
            return;
        }
    } elsif($c->user->roles eq "subscriber") {
        if ($id != $c->user->voip_subscriber->id) {
            $self->error($c, HTTP_FORBIDDEN, "Read-only resource for authenticated role");
            return;
        }
    }
    return 1;
}

sub subscriberadmin_write_access {
    my ($self, $c, $id) = @_;
    if ( (( $c->config->{privileges}->{subscriberadmin}->{subscribers}
           && $c->config->{privileges}->{subscriberadmin}->{subscribers} =~/write/
          )
         ||
          ( $c->license('pbx') && $c->config->{features}->{cloudpbx}
           && $c->user->contract->product->class eq 'pbxaccount'
          ))
         &&
         $self->check_subscriber_same_customer($c, $id)
        ) {
        return 1;
    }
    return 0;
}

sub check_subscriber_same_customer {
    my ($self, $c, $id) = @_;

    my $sub = $c->model('DB')->resultset('voip_subscribers')->find($id);

    if ($sub && $sub->status ne 'terminated' && $sub->contract_id == $c->user->account_id) {
        return 1;
    }

    return 0;
}

1;
# vim: set tabstop=4 expandtab:
