# -*- coding: utf-8 -*-

# Copyright (C) 2015-2016 Avencall
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

from flask import url_for, request
from flask_restful import reqparse, fields, marshal
from xivo_dao.alchemy.userfeatures import UserFeatures as User

from xivo_confd.authentication.confd_auth import required_acl
from xivo_confd.helpers.restful import FieldList, Link, ListResource, ItemResource, Strict, LabelIds

user_fields = {
    'id': fields.Integer,
    'uuid': fields.String,
    'firstname': fields.String,
    'lastname': fields.String,
    'email': fields.String,
    'timezone': fields.String,
    'language': fields.String,
    'description': fields.String,
    'caller_id': fields.String,
    'outgoing_caller_id': fields.String,
    'mobile_phone_number': fields.String,
    'username': fields.String,
    'password': fields.String,
    'music_on_hold': fields.String,
    'preprocess_subroutine': fields.String,
    'userfield': fields.String,
    'call_transfer_enabled': fields.Boolean,
    'call_record_enabled': fields.Boolean,
    'online_call_record_enabled': fields.Boolean,
    'supervision_enabled': fields.Boolean,
    'ring_seconds': fields.Integer,
    'simultaneous_calls': fields.Integer,
    'call_permission_password': fields.String,
    'agent_number': fields.String,
    'agent_group_id': fields.Integer(default=None),
    'agentid': fields.Integer(default=None),
    'enabled': fields.Boolean,
    'links': FieldList(Link('users')),
    'labels': LabelIds('id')
}

view_fields = {
    'directory': {
        'id': fields.Integer,
        'uuid': fields.String,
        'line_id': fields.Integer(default=None),
        'agent_id': fields.Integer(default=None),
        'firstname': fields.String,
        'lastname': fields.String,
        'email': fields.String,
        'exten': fields.String,
        'mobile_phone_number': fields.String,
        'voicemail_number': fields.String,
        'userfield': fields.String,
        'description': fields.String,
        'context': fields.String,
    },
    'summary': {
        'id': fields.Integer,
        'uuid': fields.String,
        'firstname': fields.String,
        'lastname': fields.String,
        'extension': fields.String,
        'context': fields.String,
        'provisioning_code': fields.String,
        'entity': fields.String,
        'protocol': fields.String,
        'enabled': fields.Boolean,
        'configregistrar': fields.String,
        'line_type': fields.String
    }
}

parser = reqparse.RequestParser()
parser.add_argument('firstname', type=Strict(str), store_missing=False, nullable=False)
parser.add_argument('lastname', type=Strict(str), store_missing=False)
parser.add_argument('email', type=Strict(str), store_missing=False)
parser.add_argument('timezone', type=Strict(str), store_missing=False)
parser.add_argument('language', type=Strict(str), store_missing=False)
parser.add_argument('description', type=Strict(str), store_missing=False)
parser.add_argument('outgoing_caller_id', type=Strict(str), store_missing=False)
parser.add_argument('username', type=Strict(str), store_missing=False)
parser.add_argument('password', type=Strict(str), store_missing=False)
parser.add_argument('music_on_hold', type=Strict(str), store_missing=False)
parser.add_argument('preprocess_subroutine', type=Strict(str), store_missing=False)
parser.add_argument('userfield', type=Strict(str), store_missing=False)
parser.add_argument('call_transfer_enabled', type=Strict(bool), store_missing=False)
parser.add_argument('call_record_enabled', type=Strict(bool), store_missing=False)
parser.add_argument('online_call_record_enabled', type=Strict(bool), store_missing=False)
parser.add_argument('supervision_enabled', type=Strict(bool), store_missing=False)
parser.add_argument('ring_seconds', type=int, store_missing=False)
parser.add_argument('simultaneous_calls', type=int, store_missing=False)
parser.add_argument('caller_id', type=Strict(str), store_missing=False)
parser.add_argument('mobile_phone_number', type=Strict(str), store_missing=False)
parser.add_argument('call_permission_password', type=Strict(str), store_missing=False)
parser.add_argument('enabled', type=Strict(bool), store_missing=False)
parser.add_argument('agent_number', type=Strict(str), store_missing=False)
parser.add_argument('agent_group_id', type=int, store_missing=False)
parser.add_argument('agentid', type=int, store_missing=False)
parser.add_argument('labels', type=list, location='json', store_missing=False)


class UserList(ListResource):
    model = User
    fields = user_fields
    parser = parser

    def build_headers(self, user):
        return {'Location': url_for('users', id=user.id, _external=True)}

    @required_acl('confd.users.create')
    def post(self):
        form = self.parser.parse_args()
        agent_number = None
        agent_group_id = None
        label_ids = None
        if 'agent_number' in form:
            if form['agent_number'] is not None:
                agent_number = form['agent_number']
            del form['agent_number']
            if 'agent_group_id' in form:
                agent_group_id = form['agent_group_id']
                del form['agent_group_id']

        if 'labels' in form:
            if form['labels']:
                label_ids = form['labels']
            del form['labels']

        model = self.model(**form)
        model = self.service.create_with_agent(model, agent_number, agent_group_id, label_ids)
        marshalled = marshal(model, self.fields)
        if agent_number is not None:
            marshalled['agent_number'] = agent_number
            marshalled['agent_group_id'] = agent_group_id
        return marshalled, 201, self.build_headers(model)

    @required_acl('confd.users.read')
    def get(self):
        if 'q' in request.args:
            return self.legacy_search()
        elif 'search_ng' in request.args:
            return self.user_search(ng=True)
        else:
            return self.user_search()

    def legacy_search(self):
        result = self.service.legacy_search(request.args['q'])
        return {'total': result.total,
                'items': [marshal(item, user_fields) for item in result.items]}

    def user_search(self, ng=False):
        params = self.search_params()
        if ng:
            params['search'] = params.pop('search_ng')
            search = self.service.search_ng
        else:
            search = self.service.search
        result = search(params)
        fields = view_fields.get(request.args.get('view'), user_fields)
        return {'total': result.total,
                'items': [marshal(item, fields) for item in result.items]}


class UserItem(ItemResource):
    fields = user_fields
    parser = parser

    @required_acl('confd.users.{id}.read')
    def get(self, id):
        model = self.service.get_with_agent(id)
        # get_with_agent returns a sqlalchemy result: a tuple (UserFeatures, agent_number, agent_group_id)
        # we need to marshal UserFeatures separately to benefit from the
        # methods of the UserFeatures class
        # and then we add the agent_number
        marshalled = marshal(model.UserFeatures, self.fields)
        marshalled['agent_number'] = model.agent_number
        marshalled['agent_group_id'] = model.agent_group_id
        return marshalled

    @required_acl('confd.users.{id}.update')
    def put(self, id):
        model = self.service.get(id)
        form = self.parser.parse_args()

        for name, value in form.items():
            if name == "labels":
                labels_dao = self.service.get_labels(form['labels'])
                model.labels = labels_dao
                continue
            setattr(model, name, value)

        if 'labels' in form:
            del form['labels']

        if 'agent_number' in form:
            agent_number = form['agent_number']
            agent_group_id = form['agent_group_id'] if 'agent_group_id' in form else None
            self.service.edit_with_agent(model, agent_number, agent_group_id)
        else:
            if 'agent_group_id' in form:
                self.service.edit_agent_group(model, form['agent_group_id'])
                del form['agent_group_id']
            self.service.edit(model)
        return '', 204

    @required_acl('confd.users.{id}.delete')
    def delete(self, id):
        return super(UserItem, self).delete(id)
