"""
This Service provides functionality to access and modify the
InstalledComponentsDB database
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import six

from DIRAC import S_OK, S_ERROR, gLogger

from DIRAC.FrameworkSystem.DB.InstalledComponentsDB import InstalledComponentsDB,\
    Component, Host, InstalledComponent, HostLogging
from DIRAC.Core.DISET.RequestHandler import RequestHandler

__RCSID__ = "c0787e027 (2021-05-20 13:00:51 +0200) fstagni <federico.stagni@cern.ch>"


class ComponentMonitoringHandler(RequestHandler):

  @classmethod
  def initializeHandler(cls, serviceInfo):
    """
    Handler class initialization
    """

    ComponentMonitoringHandler.doCommit = True

    try:
      ComponentMonitoringHandler.db = InstalledComponentsDB()
    except Exception as e:
      gLogger.error('Could not connect to the database', ': %s' % (e))
      return S_ERROR('Could not connect to the database')

    return S_OK('Initialization went well')

  def __joinInstallationMatch(self,
                              installationFields,
                              componentFields,
                              hostFields):
    matchFields = installationFields
    for key in componentFields:
      matchFields['Component.' + key] = componentFields[key]
    for key in hostFields:
      matchFields['Host.' + key] = hostFields[key]

    return S_OK(matchFields)

  types_addComponent = [dict]

  def export_addComponent(self, component):
    """
    Creates a new Component object on the database
    component argument should be a dictionary with the Component fields and
    its values
    """

    return ComponentMonitoringHandler.db.addComponent(component)

  types_componentExists = [dict]

  def export_componentExists(self, matchFields):
    """
    Returns whether components matching the given criteria exist
    matchFields argument should be a dictionary with the fields to match
    matchFields accepts fields of the form <Field.bigger> and <Field.smaller>
    to filter using > and < relationships.
    """

    return ComponentMonitoringHandler.db.exists(Component, matchFields)

  types_getComponents = [dict, bool, bool]

  def export_getComponents(self,
                           matchFields,
                           includeInstallations,
                           includeHosts):
    """
    Returns a list of all the Components in the database
    matchFields argument should be a dictionary with the fields to match or
    empty to get all the instances
    matchFields also accepts fields of the form <Field.bigger> and
    <Field.smaller> to filter using > and < relationships
    includeInstallations indicates whether data about the installations in
    which the components takes part is to be retrieved
    includeHosts (only if includeInstallations is set to True) indicates
    whether data about the host in which there are instances of this component
    is to be retrieved
    """

    return ComponentMonitoringHandler.db.getComponents(matchFields, includeInstallations, includeHosts)

  types_updateComponents = [dict, dict]

  def export_updateComponents(self, matchFields, updates):
    """
    Updates Components objects on the database
    matchFields argument should be a dictionary with the fields to match
    (instances matching the fields will be updated) or empty to update all
    the instances
    matchFields also accepts fields of the form <Field.bigger> and
    <Field.smaller> to filter using > and < relationships updates argument
    updates should be a dictionary with the Component fields and their new
    updated values
    """

    return ComponentMonitoringHandler.db.updateComponents(matchFields, updates)

  types_removeComponents = [dict]

  def export_removeComponents(self, matchFields):
    """
    Removes from the database components that match the given fields
    matchFields argument should be a dictionary with the fields to match or
    empty to remove all the instances
    matchFields also accepts fields of the form <Field.bigger> and
    <Field.smaller> to filter using > and < relationships
    """

    return ComponentMonitoringHandler.db.removeComponents(matchFields)

  types_addHost = [dict]

  def export_addHost(self, host):
    """
    Creates a new Host object on the database
    host argument should be a dictionary with the Host fields and its values
    """

    return ComponentMonitoringHandler.db.addHost(host)

  types_hostExists = [dict]

  def export_hostExists(self, matchFields):
    """
    Returns whether hosts matching the given criteria exist
    matchFields argument should be a dictionary with the fields to match
    matchFields also accepts fields of the form <Field.bigger> and
    <Field.smaller> to filter using > and < relationships
    """

    return ComponentMonitoringHandler.db.exists(Host, matchFields)

  types_getHosts = [dict, bool, bool]

  def export_getHosts(self,
                      matchFields,
                      includeInstallations,
                      includeComponents):
    """
    Returns a list of all the Hosts in the database
    matchFields argument should be a dictionary with the fields to match or
    empty to get all the instances
    matchFields also accepts fields of the form <Field.bigger> and
    <Field.smaller> to filter using > and < relationships
    includeInstallations indicates whether data about the installations in
    which the host takes part is to be retrieved
    includeComponents (only if includeInstallations is set to True) indicates
    whether data about the components installed into this host is to
    be retrieved
    """

    return ComponentMonitoringHandler.db.getHosts(matchFields, includeInstallations, includeComponents)

  types_updateHosts = [dict, dict]

  def export_updateHosts(self, matchFields, updates):
    """
    Updates Hosts objects on the database
    matchFields argument should be a dictionary with the fields to
    match (instances matching the fields will be updated) or empty to update
    all the instances
    matchFields also accepts fields of the form <Field.bigger> and
    <Field.smaller> to filter using > and < relationships updates argument
    should be a dictionary with the Host fields and their new updated values
    updates argument should be a dictionary with the Host fields and
    their new updated values
    """

    return ComponentMonitoringHandler.db.updateHosts(matchFields, updates)

  types_removeHosts = [dict]

  def export_removeHosts(self, matchFields):
    """
    Removes from the database hosts that match the given fields
    matchFields argument should be a dictionary with the fields to match or
    empty to remove all the instances matchFields also accepts fields of the
    form <Field.bigger> and <Field.smaller> to filter
    using > and < relationships
    """

    return ComponentMonitoringHandler.db.removeHosts(matchFields)

  types_addInstallation = [dict,
                           dict,
                           dict,
                           bool]

  def export_addInstallation(self,
                             installation,
                             componentDict,
                             hostDict,
                             forceCreate):
    """
    Creates a new InstalledComponent object on the database
    installation argument should be a dictionary with the InstalledComponent
    fields and its values
    componentDict argument should be a dictionary with the Component fields
    and its values
    hostDict argument should be a dictionary with the Host fields and
    its values
    forceCreate indicates whether a new Component and/or Host should be
    created if the given ones do not exist
    """

    return ComponentMonitoringHandler.db.addInstalledComponent(installation, componentDict, hostDict, forceCreate)

  types_installationExists = [dict, dict, dict]

  def export_installationExists(self,
                                installationFields,
                                componentFields,
                                hostFields):
    """
    Returns whether installations matching the given criteria exist
    installationFields argument should be a dictionary with the fields to
    match for the installation
    componentFields argument should be a dictionary with the fields to match
    for the component installed
    hostFields argument should be a dictionary with the fields to match for
    the host where the installation is made
    """

    matchFields = self.__joinInstallationMatch(installationFields, componentFields, hostFields)['Value']

    return ComponentMonitoringHandler.db.exists(InstalledComponent, matchFields)

  types_getInstallations = [dict,
                            dict,
                            dict,
                            bool]

  def export_getInstallations(self,
                              installationFields,
                              componentFields,
                              hostFields,
                              installationsInfo):
    """
    Returns a list of installations matching the given criteria
    installationFields argument should be a dictionary with the fields to
    match for the installation
    componentFields argument should be a dictionary with the fields to match
    for the component installed
    hostFields argument should be a dictionary with the fields to match for
    the host where the installation is made
    installationsInfo indicates whether information about the components and
    host taking part in the installation is to be provided
    """

    matchFields = self.__joinInstallationMatch(installationFields, componentFields, hostFields)['Value']

    return ComponentMonitoringHandler.db.getInstalledComponents(matchFields, installationsInfo)

  types_updateInstallations = [dict,
                               dict,
                               dict,
                               dict]

  def export_updateInstallations(self,
                                 installationFields,
                                 componentFields,
                                 hostFields,
                                 updates):
    """
    Updates installations matching the given criteria
    installationFields argument should be a dictionary with the fields to
    match for the installation
    componentFields argument should be a dictionary with the fields to match
    for the component installed or empty to update regardless of component
    hostFields argument should be a dictionary with the fields to match for
    the host where the installation is made or empty to update
    regardless of host
    updates argument should be a dictionary with the Installation fields and
    their new updated values
    """

    matchFields = self.__joinInstallationMatch(installationFields, componentFields, hostFields)['Value']

    return ComponentMonitoringHandler.db.updateInstalledComponents(matchFields, updates)

  types_removeInstallations = [dict, dict, dict]

  def export_removeInstallations(self,
                                 installationFields,
                                 componentFields,
                                 hostFields):
    """
    Removes installations matching the given criteria
    installationFields argument should be a dictionary with the fields to
    match for the installation
    componentFields argument should be a dictionary with the fields to match
    for the component installed
    hostFields argument should be a dictionary with the fields to match for
    the host where the installation is made
    """

    matchFields = self.__joinInstallationMatch(installationFields, componentFields, hostFields)['Value']

    return \
        ComponentMonitoringHandler.db.removeInstalledComponents(matchFields)

  types_updateLog = [six.string_types, dict]

  def export_updateLog(self, host, fields):
    """
    Updates the log entry for a given host in the database with the given fields
    host is the name of the machine to which the logging information belongs
    fields is a dictionary where the fields contain the logging information to be stored in the database
    """
    result = ComponentMonitoringHandler.db.exists(HostLogging, {'hostName': host})
    if not result['OK']:
      return result

    if result['Value']:
      result = ComponentMonitoringHandler.db.updateLogs({'hostName': host}, fields)
    else:
      fields['hostName'] = host
      result = ComponentMonitoringHandler.db.addLog(fields)

    if not result['OK']:
      return result

    return S_OK('Logs updated correctly')

  types_getLog = [six.string_types]

  def export_getLog(self, host):
    """
    Retrieves the logging information currently stored for the given host
    """
    result = ComponentMonitoringHandler.db.exists(HostLogging, {'hostName': host})
    if not result['OK']:
      return result
    if not result['Value']:
      return S_ERROR('Host %s does not exist' % host)

    return ComponentMonitoringHandler.db.getLogs({'hostName': host})

  types_getLogs = []

  def export_getLogs(self):
    """
    Retrieves the logging information currently stored for all hosts
    """
    return ComponentMonitoringHandler.db.getLogs()

  types_removeLogs = [dict]

  def export_removeLogs(self, fields):
    """
    Deletes all the matching logging information
    fields is a dictionary containing the values for the fields
    such that any matching entries in the database should be deleted
    """
    result = ComponentMonitoringHandler.db.exists(HostLogging, fields)
    if not result['OK']:
      return result
    if not result['Value']:
      return S_ERROR('Host does not exist')

    return ComponentMonitoringHandler.db.removeLogs(fields)