# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 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/>

import logging
import time

from xivo_dao import session_dao, admin_dao
from xivo_dao.helpers.db_utils import session_scope

from xivo_auth import BaseAuthenticationBackend
from xivo_auth.plugins.backends.webi_perms_to_acl import WEBI_PERMS_TO_CONFD_ACL

from phpserialize import *

logger = logging.getLogger(__name__)


class XiVOSession(BaseAuthenticationBackend):

    def get_acls(self, login, args):
        with session_scope():
            username = self._get_username(args['cookie'])
            if self._is_root(username):
                return ['confd.#']
            else:
                return self._get_acl_for_webi_user(username)

    def get_ids(self, username, args):
        with session_scope():
            username = self._get_username(args['cookie'])
            auth_id = str(admin_dao.get_admin_id(username))
        user_uuid = None
        return auth_id, user_uuid

    def verify_password(self, login, password, args):
        cookie = args['cookie']
        if cookie is not None:
            with session_scope():
                current_timestamp = int(time.time())
                session_obj = session_dao.get_session(args['cookie'])
                if current_timestamp < session_obj.expire and self._deserialize_username(session_obj.data) is not None:
                    return True
                else:
                    return False
        else:
            return False

    def _get_username(self, cookie):
        session_obj = session_dao.get_session(cookie)
        username = self._deserialize_username(session_obj.data)
        logger.debug("Retrieved username %s " % username)
        return username if username is not None else None

    def _get_acl_for_webi_user(self, username):
        webi_user_perms = self._get_webiuser_permission(username)
        return self._get_acl_from_webi_perms(webi_user_perms)

    def _get_webiuser_permission(self, username):
        webi_user_data = admin_dao.get_admin_permission(username)
        return self._deserialize_webi_permission(webi_user_data)

    def _deserialize_webi_permission(self, webi_user_data):
        try:
            return loads(webi_user_data)
        except Exception, e:
            logger.error("Failed to deserialize user data: %s " % str(e))
            return None

    def _get_acl_from_webi_perms(self, webi_user_perms):
        users_acl = []

        if not webi_user_perms:
            return users_acl

        webi_user_perms_flattened = self._flatten_webi_user_perms(webi_user_perms)
        for webi_path, authorization in webi_user_perms_flattened.items():
            if authorization:
                if webi_path in WEBI_PERMS_TO_CONFD_ACL:
                    users_acl.append(WEBI_PERMS_TO_CONFD_ACL[webi_path])
                else:
                    logger.debug("Couldn't get confd ACL for webi path %s: mapping undefined" % webi_path)

        return list(set(users_acl))

    def _flatten_webi_user_perms(self, webi_user_perms):
        def flatten(d, parent_key='', sep='.'):
            items = []
            for k, v in d.items():
                new_key = parent_key + sep + k if parent_key else k
                if isinstance(v, dict):
                    items.extend(flatten(v, new_key, sep=sep).items())
                else:
                    items.append((new_key, v))
            return dict(items)
        return flatten(webi_user_perms)

    @staticmethod
    def _deserialize_username(session_data):
        try:
            serialized_data = session_data.split('_USR|', 5)[1].split("_WEBI", 5)[0]
            deserialized_user = loads(serialized_data, object_hook=phpobject)
            return deserialized_user.login
        except Exception, e:
            logger.error("Failed to deserialize session data: %s " % str(e))
            return None

    @staticmethod
    def _is_root(username):
        if username == 'root':
            return True
        elif username is None:
            raise ValueError('username must not be None')
        else:
            return False
