# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida-core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################
"""SQLA Log and LogCollection module"""
# pylint: disable=import-error,no-name-in-module
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

from sqlalchemy.orm.exc import NoResultFound

from aiida.backends.sqlalchemy import get_scoped_session
from aiida.backends.sqlalchemy.models import log as models
from aiida.common import exceptions

from .. import BackendLog, BackendLogCollection
from . import entities
from . import utils


class SqlaLog(entities.SqlaModelEntity[models.DbLog], BackendLog):
    """SQLA Log backend entity"""

    MODEL_CLASS = models.DbLog

    def __init__(self, backend, time, loggername, levelname, dbnode_id, message='', metadata=None):
        # pylint: disable=too-many-arguments
        super(SqlaLog, self).__init__(backend)
        self._dbmodel = utils.ModelWrapper(
            models.DbLog(
                time=time,
                loggername=loggername,
                levelname=levelname,
                dbnode_id=dbnode_id,
                message=message,
                metadata=metadata
            )
        )

    @property
    def uuid(self):
        """
        Get the string representation of the UUID of the log entry
        """
        return str(self._dbmodel.uuid)

    @property
    def time(self):
        """
        Get the time corresponding to the entry
        """
        return self._dbmodel.time

    @property
    def loggername(self):
        """
        The name of the logger that created this entry
        """
        return self._dbmodel.loggername

    @property
    def levelname(self):
        """
        The name of the log level
        """
        return self._dbmodel.levelname

    @property
    def dbnode_id(self):
        """
        Get the id of the object that created the log entry
        """
        return self._dbmodel.dbnode_id

    @property
    def message(self):
        """
        Get the message corresponding to the entry
        """
        return self._dbmodel.message

    @property
    def metadata(self):
        """
        Get the metadata corresponding to the entry
        """
        return self._dbmodel._metadata  # pylint: disable=protected-access


class SqlaLogCollection(BackendLogCollection):
    """The SQLA collection for logs"""

    ENTITY_CLASS = SqlaLog

    def delete(self, log_id):
        """
        Remove a Log entry from the collection with the given id

        :param log_id: id of the Log to delete
        :type log_id: int

        :raises TypeError: if ``log_id`` is not an `int`
        :raises `~aiida.common.exceptions.NotExistent`: if Log with ID ``log_id`` is not found
        """
        if not isinstance(log_id, int):
            raise TypeError('log_id must be an int')

        session = get_scoped_session()

        try:
            session.query(models.DbLog).filter_by(id=log_id).one().delete()
            session.commit()
        except NoResultFound:
            session.rollback()
            raise exceptions.NotExistent("Log with id '{}' not found".format(log_id))

    def delete_all(self):
        """
        Delete all Log entries.

        :raises `~aiida.common.exceptions.IntegrityError`: if all Logs could not be deleted
        """
        session = get_scoped_session()

        try:
            session.query(models.DbLog).delete()
            session.commit()
        except Exception as exc:
            session.rollback()
            raise exceptions.IntegrityError('Could not delete all Logs. Full exception: {}'.format(exc))

    def delete_many(self, filters):
        """
        Delete Logs based on ``filters``

        :param filters: similar to QueryBuilder filter
        :type filters: dict

        :return: (former) ``PK`` s of deleted Logs
        :rtype: list

        :raises TypeError: if ``filters`` is not a `dict`
        :raises `~aiida.common.exceptions.ValidationError`: if ``filters`` is empty
        """
        from aiida.orm import Log, QueryBuilder

        # Checks
        if not isinstance(filters, dict):
            raise TypeError('filters must be a dictionary')
        if not filters:
            raise exceptions.ValidationError('filter must not be empty')

        # Apply filter and delete found entities
        builder = QueryBuilder().append(Log, filters=filters, project='id').all()
        entities_to_delete = [_[0] for _ in builder]
        for entity in entities_to_delete:
            self.delete(entity)

        # Return list of deleted entities' (former) PKs for checking
        return entities_to_delete
