package NGCP::Panel::Utils::Phonebook;
use strict;
use warnings;

use Sipwise::Base;
use English;
use NGCP::Panel::Utils::Generic qw(:all);
use NGCP::Panel::Utils::Message;

sub get_reseller_phonebook {
    my ($c, $reseller_id) = @_;
    my @pb;

    my $r_pb_rs = $c->model('DB')->resultset('reseller_phonebook')->search({
        reseller_id => $reseller_id,
    });

    for my $r ($r_pb_rs->all) {
        push @pb, { name => $r->name, number => $r->number };
    }

    return [ sort { $a->{name} cmp $b->{name} } @pb ];
}

sub get_contract_phonebook {
    my ($c, $contract_id) = @_;
    my @pb;
    my %c_numbers;

    my $contract = $c->model('DB')->resultset('contracts')->search({
        id => $contract_id,
    })->first;

    my $r_pb_rs = $c->model('DB')->resultset('reseller_phonebook')->search({
        reseller_id => $contract->contact->reseller->id,
    });

    my $c_pb_rs = $c->model('DB')->resultset('contract_phonebook')->search({
        contract_id => $contract_id,
    });

    for my $r ($c_pb_rs->all) {
        push @pb, { name => $r->name, number => $r->number };
        $c_numbers{$r->number} = $r->name;
    }

    for my $r ($r_pb_rs->all) {
        unless (exists $c_numbers{$r->number}) {
            push @pb, { name => $r->name, number => $r->number };
        }
    }

    return [ sort { $a->{name} cmp $b->{name} } @pb ];
}

sub get_subscriber_phonebook {
    my ($c, $subscriber_id) = @_;
    my @pb;
    my %c_numbers;

    my $sub = $c->model('DB')->resultset('voip_subscribers')->search({
        id => $subscriber_id,
    })->first;

    my $r_pb_rs = $c->model('DB')->resultset('reseller_phonebook')->search({
        reseller_id => $sub->contract->contact->reseller->id,
    });

    my $c_pb_rs = $c->model('DB')->resultset('contract_phonebook')->search({
        contract_id => $sub->contract_id,
    });

    my $a_pb_rs = $c->model('DB')->resultset('subscriber_phonebook')->search({
        shared => 1,
        'contract.id' => $sub->contract_id,
    },{
        join => { 'subscriber' => 'contract' },
    });

    my $s_pb_rs = $c->model('DB')->resultset('subscriber_phonebook')->search({
        subscriber_id => $subscriber_id,
    });

    for my $r ($s_pb_rs->all) {
        push @pb, { name => $r->name, number => $r->number };
        $c_numbers{$r->number} = $r->name;
    }

    for my $r ($c_pb_rs->all) {
        unless (exists $c_numbers{$r->number}) {
            push @pb, { name => $r->name, number => $r->number };
            $c_numbers{$r->number} = $r->name;
        }
    }

    for my $r ($a_pb_rs->all) {
        unless (exists $c_numbers{$r->number}) {
            push @pb, { name => $r->name, number => $r->number };
            $c_numbers{$r->number} = $r->name;
        }
    }

    for my $r ($r_pb_rs->all) {
        unless (exists $c_numbers{$r->number}) {
            push @pb, { name => $r->name, number => $r->number };
        }
    }

    return [ sort { $a->{name} cmp $b->{name} } @pb ];
}

sub ui_upload_csv {
    my ($c, $rs, $form, $owner, $owner_id, $action, $back) = @_;

    my $upload = $c->req->upload('upload_phonebook');
    my $posted = $c->req->method eq 'POST';
    my @params = ( upload_phonebook => $posted ? $upload : undef, );
    $form->process(
        posted => $posted,
        params => { @params },
        action => $action,
    );
    if($form->validated) {
        unless($upload) {
            NGCP::Panel::Utils::Message::error(
                c    => $c,
                desc => $c->loc('No phonebook entries file specified!'),
            );
            $c->response->redirect($back);
            return;
        }
        my $data = $upload->slurp;
        my($entries, $fails, $text_success);
        try {
            my ($entries, $fails, $text) =
                upload_csv($c, $rs, $owner, $owner_id,
                           $c->req->params->{purge_existing}, \$data);
            NGCP::Panel::Utils::Message::info(
                c    => $c,
                desc => $$text,
            );
        } catch($e) {
            NGCP::Panel::Utils::Message::error(
                c => $c,
                error => $e,
                desc => $c->loc('Failed to upload Phonebook entries'),
            );
        }

        $c->response->redirect($back);
    }

    return;
}

sub upload_csv {
    my ($c, $rs, $owner, $owner_id, $purge, $data) = @_;

    my $csv = Text::CSV_XS->new({ allow_whitespace => 1, binary => 1, keep_meta_info => 1 });

    my $schema = $c->model('DB');

    my @cols = qw/name number/;
    my @fields ;
    my @fails = ();
    my $linenum = 0;
    my @entries;

    my $no_access = 0;
    my %contract_ids;
    my %subscriber_ids;
    # check user access to owner_id
    if ($c->user->roles eq 'reseller') {
        if ($owner_id && $owner eq 'reseller' && $owner_id != $c->user->reseller_id) {
            $no_access = 1;
        } elsif ($owner eq 'contract') {
            my $found = 0;
            foreach my $row ($schema->resultset('contracts')->search({
                'contact.reseller_id' => $c->user->reseller_id,
            },{
                select => ['me.id'],
                as => ['id'],
                join => 'contact',
                result_class => 'DBIx::Class::ResultClass::HashRefInflator',
            })->all) {
                my $id = $row->{id};
                if ($owner_id) {
                    if ($id == $owner_id) {
                        $found = 1;
                        last;
                    }
                } else {
                    $contract_ids{$id} = 1;
                }
            }
            if ($owner_id) {
                $no_access = 1 unless $found;
            }
        } elsif ($owner eq 'subscriber') {
            my $found = 0;
            foreach my $row ($schema->resultset('voip_subscribers')->search({
                'contact.reseller_id' => $c->user->reseller_id,
            },{
                select => ['me.id'],
                as => ['id'],
                join => { 'contract' => 'contact' },
                result_class => 'DBIx::Class::ResultClass::HashRefInflator',
            })->all) {
                my $id = $row->{id};
                if ($owner_id) {
                    if ($id == $owner_id) {
                        $found = 1;
                        last;
                    }
                } else {
                    $subscriber_ids{$id} = 1;
                }
            }
            if ($owner_id) {
                $no_access = 1 unless $found;
            }
        }
    } elsif ($c->user->roles eq 'subscriberadmin') {
        if ($owner_id && $owner eq 'reseller') {
            $no_access = 1;
        } elsif ($owner_id && $owner eq 'contract' && $owner_id != $c->user->account_id) {
            $no_access = 1;
        } elsif ($owner eq 'subscriber') {
            my $found = 0;
            foreach my $row ($schema->resultset('voip_subscribers')->search({
                'contract_id' => $c->user->account_id,
            },{
                select => ['me.id'],
                as => ['id'],
                result_class => 'DBIx::Class::ResultClass::HashRefInflator',
            })->all) {
                my $id = $row->{id};
                if ($owner_id) {
                    if ($id == $owner_id) {
                        $found = 1;
                        last;
                    }
                } else {
                    $subscriber_ids{$id} = 1;
                }
            }
            if ($owner_id) {
                $no_access = 1 unless $found;
            }
        }
    } elsif ($owner_id && $c->user->roles eq 'subscriber') {
        if ($owner eq 'reseller') {
            $no_access = 1;
        } elsif ($owner eq 'contract') {
            $no_access = 1;
        } elsif ($owner eq 'subscriber' && $owner_id != $c->user->voip_subscriber->id) {
            $no_access = 1;
        }
    }

    if ($no_access) {
        my $accepted = 0;
        my (@entries, @fails);
        my $text = 'Phonebook entries upload failed: ';
           $text .= "user does not have access to $owner with id $owner_id\n";
        return (\@entries, \@fails, \$text);
    }

    $schema->txn_do(sub {

        open(my $fh, '<:encoding(utf8)', $data);

        while ( my $line = <$fh> ){
            ++$linenum;
            next unless length $line;
            unless($csv->parse($line)) {
                push @fails, $linenum;
                next;
            }
            @fields = $csv->fields();

            my $row = {};

            if ($owner_id) {
                # name,number
                if ($owner ne 'subscriber' && scalar @fields >= 2) {
                    @{$row}{@cols} = splice @fields, 0, 2;
                # name,number,shared
                } elsif ($owner eq 'subscriber' && scalar @fields >= 3) {
                    my $shared = int($fields[2]);
                    if ($shared != 1 && $shared != 0) {
                        push @fails, $linenum;
                        next;
                    }
                    @{$row}{@cols,'shared'} = splice @fields, 0, 3;
                } else {
                    push @fails, $linenum;
                    next;
                }
                $row->{$owner.'_id'} = $owner_id;
            } else {
                if ($owner eq 'reseller') {
                    # name,number,reseller_id
                    if (scalar @fields != 3) {
                        push @fails, $linenum;
                        next;
                    }
                    my $reseller_id_f = int($fields[2]);
                    if ($c->user->roles eq 'admin') {
                        @{$row}{@cols,'reseller_id'} = @fields;
                    } elsif ($c->user->roles eq 'reseller' &&
                             $reseller_id_f == $c->user->reseller_id) {
                        @{$row}{@cols,'reseller_id'} = @fields;
                    } else {
                        push @fails, $linenum;
                        next;
                    }
                } elsif ($owner eq 'contract') {
                    # name,number,contract_id
                    if (scalar @fields != 3) {
                        push @fails, $linenum;
                        next;
                    }
                    my $contract_id_f = int($fields[2]);
                    if ($c->user->roles eq 'admin') {
                        @{$row}{@cols,'contract_id'} = @fields;
                    } elsif ($c->user->roles eq 'reseller' &&
                             exists $contract_ids{$contract_id_f}) {
                        @{$row}{@cols,'contract_id'} = @fields;
                    } elsif ($c->user->roles eq 'subscriberadmin' &&
                             $contract_id_f == $c->user->account_id) {
                        @{$row}{@cols,'contract_id'} = @fields;
                    } else {
                        push @fails, $linenum;
                        next;
                    }
                } elsif ($owner eq 'subscriber') {
                    # name,number,shared,subscriber_id
                    if (scalar @fields != 4) {
                        push @fails, $linenum;
                        next;
                    }
                    my $subscriber_id_f = int($fields[3]);
                    my $shared = int($fields[2]);
                    if ($shared != 1 && $shared != 0) {
                        push @fails, $linenum;
                        next;
                    }
                    if ($c->user->roles eq 'admin') {
                        @{$row}{@cols,'shared','subscriber_id'} = @fields;
                    } elsif ($c->user->roles eq 'reseller' &&
                             exists $subscriber_ids{$subscriber_id_f}) {
                        @{$row}{@cols,'shared','subscriber_id'} = @fields;
                    } elsif ($c->user->roles eq 'subscriberadmin' &&
                             exists $subscriber_ids{$subscriber_id_f}) {
                        @{$row}{@cols,'shared','subscriber_id'} = @fields;
                    } elsif ($c->user->roles eq 'subscriber' &&
                             $subscriber_id_f == $c->user->voip_subscriber->id) {
                        @{$row}{@cols,'shared','subscriber_id'} = @fields;
                    } else {
                        push @fails, $linenum;
                        next;
                    }
                } else {
                    push @fails, $linenum;
                    next;
                }
            }

            unless ($purge) {
                try {
                    $rs->update_or_create($row,{key=>'rel_u_idx'});
                } catch($e) {
                    push @fails, $linenum;
                    next;
                }
            }
            push @entries, $row;
        }

        if ($purge) {
            my ($start, $end);
            $start = time;
            $rs->delete;
            $end = time;
            $c->log->debug("Purging phonebook entries took " . ($end - $start) . "s");
            $start = time;
            $rs->populate(\@entries);
            $end = time;
            $c->log->debug("Populating phonebook entries took " . ($end - $start) . "s");
        }
    });

    my $text = $c->loc('Phonebook entries successfully uploaded');
    if (@fails) {
        $text .= $c->loc(", but skipped the following line numbers: ") . (join ", ", @fails);
    }

    return ( \@entries, \@fails, \$text );
}

sub download_csv {
    my ($c, $rs, $owner) = @_;

    my @cols = qw/name number/;

    if ($owner eq 'reseller' && $c->user->roles eq 'admin') {
        push @cols, 'reseller_id';
    } elsif ($owner eq 'subscriber') {
        push @cols, 'shared';
    }

    my ($start, $end);
    $start = time;
    foreach my $row ($rs->all) {
        my %entry = $row->get_inflated_columns;
        delete $entry{id};
        $c->res->write_fh->write_encoded(join (",", @entry{@cols}) );
        $c->res->write_fh->write("\n");
    }
    $c->res->write_fh->close;
    $end = time;
    $c->log->debug("Creating phonebook entries CSV for download took " . ($end - $start) . "s");
    return 1;
}

1;

=head1 NAME

NGCP::Panel::Utils::Phonebook

=head1 DESCRIPTION

A helper to manipulate the phonebook data

=head1 METHODS

=head2 get_reseller_phonebook

=head2 get_contract_phonebook

=head2 get_subscriber_phonebook

=head1 AUTHOR

Sipwise Development Team

=head1 LICENSE

This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
# vim: set tabstop=4 expandtab:
