# -*- 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


import abc
from typing import Any, Optional

from xivo_dao.alchemy.extension import Extension
from xivo_dao.alchemy.linefeatures import LineFeatures as Line
from xivo_dao.alchemy.sccpline import SCCPLine as SCCP
from xivo_dao.alchemy.userfeatures import UserFeatures as User
from xivo_dao.alchemy.usersip import UserSIP as SIP
from xivo_dao.helpers.exception import NotFoundError
from xivo_dao.resources.voicemail.model import Voicemail

from xivo_confd.database import sip_update as sip_update_db


class Creator(metaclass=abc.ABCMeta):

    def __init__(self, service):
        self.service = service

    @abc.abstractmethod
    def find(self, fields: dict[str, Any]) -> Any:
        pass

    @abc.abstractmethod
    def create(self, fields: dict[str, Any]) -> Any:
        pass

    def update(self, fields: dict[str, Any], model):
        self.update_model(fields, model)
        self.service.edit(model)

    def update_model(self, fields: dict[str, Any], model):
        for key, value in fields.items():
            setattr(model, key, value)


class ImportCreator(Creator, metaclass=abc.ABCMeta):

    def update(self, fields: dict[str, Any], model, reload_device=True):
        self.update_model(fields, model)
        self.service.edit(model, reload_device)


class UserCreator(ImportCreator):

    def find(self, fields: dict[str, Any]):
        if 'uuid' in fields:
            return self.service.get_by(uuid=fields['uuid'])

    def create(self, fields):
        if fields:
            label_names = fields.pop('labels', None)
            model = User(**fields)
            for label in self.service.get_labels_by_name(label_names):
                model.labels.append(label)
            return self.service.create_with_agent(model, None, None, None)

    def update(self, fields: dict[str, Any], model, reload_device=True):
        label_names = fields.pop('labels', None)
        if label_names is not None:
            model.labels = []
            for label in self.service.get_labels_by_name(label_names):
                model.labels.append(label)
        self.update_model(fields, model)
        self.service.edit(model)


class VoicemailCreator(Creator):

    def find(self, fields: dict[str, Any]):
        number = fields.get('number')
        context = fields.get('context')
        if number or context:
            try:
                return self.service.dao.get_by_number_context(number, context)
            except NotFoundError:
                return None

    def create(self, fields: dict[str, Any]):
        number = fields.get('number')
        context = fields.get('context')
        if number or context:
            return self.service.create(Voicemail(**fields))


class LineCreator(ImportCreator):

    def find(self, fields: dict[str, Any]):
        return None

    def update(self, fields: dict[str, Any], model, reload_device=True):
        fields = dict(fields)
        if 'endpoint' in fields:
            del fields['endpoint']
        self.update_model(fields, model)
        self.service.edit(model, reload_device)

    def create(self, fields: dict[str, Any]):
        fields = dict(fields)
        context = fields.get('context')
        endpoint = fields.pop('endpoint', None)
        if context and endpoint in ('sip', 'sccp', 'webrtc', 'ua'):
            return self.service.create(Line(**fields))


class SipCreator(ImportCreator):

    def list_to_dict(self, list: list[Any]) -> dict[str, Any]:
        result = {}
        for tuple in list:
            [key, value] = tuple
            result[key] = value
        return result

    def dict_to_list(self, dict: dict[str, Any]) -> list[Any]:
        result = []
        for key, value in dict.items():
            result.append([key, value])
        return result
    
    def sip_option_update_webrtc_with_line_protocol(self, sip_options: dict[str, Any], line_protocol: Optional[str]) -> dict[str, Any]:
        if line_protocol in ("sip", "webrtc", "ua"):
            if line_protocol == "webrtc":
                sip_options["webrtc"] = "yes"
            elif line_protocol == "ua":
                sip_options["webrtc"] = "ua"
            else:
                sip_options.pop("webrtc", None)
        return sip_options 

    def find(self, fields: dict[str, Any]):
        name = fields.get('name')
        if name:
            return self.service.find_by(name=name)

    def create(self, fields: dict[str, Any]):
        line_protocol = fields.pop('endpoint', None)
        sip_options: dict[str, Any] = fields.pop('sip_options', {})

        fields['options'] = self.dict_to_list(self.sip_option_update_webrtc_with_line_protocol(sip_options, line_protocol))

        return self.service.create(SIP(**fields))

    def update(self, fields: dict[str, Any], model: object, reload_device=True):
        line_protocol = fields.pop('endpoint', None)
        sip_options: Optional[dict[str, Any]] = fields.pop('sip_options', None)
        current_sip_options: dict[str, Any] = self.list_to_dict(sip_update_db.get_sip_options(model.id)[0])
        
        has_sip_options_input: bool = sip_options is not None

        if has_sip_options_input:
            sip_options.pop("webrtc", None)
            if "webrtc" in current_sip_options.keys():
                sip_options["webrtc"] = current_sip_options["webrtc"]
            updated_sip_options = self.sip_option_update_webrtc_with_line_protocol(sip_options, line_protocol)
        else:
            updated_sip_options = self.sip_option_update_webrtc_with_line_protocol(current_sip_options, line_protocol)

        fields["options"] = self.dict_to_list(updated_sip_options)

        self.update_model(fields, model)
        self.service.edit(model, reload_device)


class SccpCreator(Creator):

    def find(self, fields: dict[str, Any]):
        return None

    def create(self, fields: dict[str, Any]):
        return self.service.create(SCCP(**fields))


class ExtensionCreator(ImportCreator):

    def find(self, fields: dict[str, Any]):
        exten = fields.get('exten')
        context = fields.get('context')
        if exten and context:
            try:
                return self.service.dao.get_by(exten=exten, context=context)
            except NotFoundError:
                return None

    def create(self, fields: dict[str, Any]):
        exten = fields.get('exten')
        context = fields.get('context')
        if exten and context:
            return self.service.create(Extension(**fields))


class CtiProfileCreator(Creator):

    def __init__(self, dao):
        self.dao = dao

    def find(self, fields: dict[str, Any]):
        name = fields.get('name')
        if name:
            try:
                cti_profile_id = self.dao.get_id_by_name(name)
                return self.dao.get(cti_profile_id)
            except NotFoundError:
                return None

    def update(self, fields: dict[str, Any], model):
        pass

    def create(self, fields: dict[str, Any]):
        name = fields.get('name')
        if name:
            cti_profile_id = self.dao.get_id_by_name(name)
            return self.dao.get(cti_profile_id)


class IncallCreator(Creator):

    def find(self, fields: dict[str, Any]):
        exten = fields.get('exten')
        context = fields.get('context')
        if exten and context:
            try:
                return self.service.dao.get_by(exten=exten, context=context)
            except NotFoundError:
                return None

    def create(self, fields: dict[str, Any]):
        fields = self.extract_extension_fields(fields)
        if fields:
            extension = Extension(**fields)
            return self.service.create(extension)

    def update(self, fields: dict[str, Any], model):
        extension_fields = self.extract_extension_fields(fields)
        self.update_model(extension_fields, model)
        self.service.edit(model)

    def extract_extension_fields(self, fields: dict[str, Any]):
        return {key: fields[key]
                for key in ('exten', 'context')
                if fields.get(key) is not None}


class CallPermissionCreator(Creator):

    def find(self, fields: dict[str, Any]):
        names = fields.get('names')
        if names is not None:
            return [self.service.get_by(name=name) for name in names]

    def create(self, fields: dict[str, Any]):
        return self.find(fields)

    def update(self, fields: dict[str, Any], model):
        pass
