import logging
import socket

from celery import shared_task
from opcua import Client

from incident.models import Incident
from incident_export.models import SyslogReceiver, OpcuaReceiver
from incident_export.services.export import incident_to_cef

_log = logging.getLogger(__name__)


def export_incident_syslog(incident):
    """ Function for sending event (incident for now) to syslog receivers
    :param incident: Incident object for sending
    """
    protocol_socktype_map = {
        'UDP': socket.SOCK_DGRAM,
        'TCP': socket.SOCK_STREAM,
    }
    # Getting syslog receivers
    syslog_receivers = SyslogReceiver.objects.all()
    my_logger = logging.getLogger('ARMASyslogExportLogger')
    # We will pass the message as INFO
    my_logger.setLevel(logging.INFO)
    # Forming incident string
    cef_message = incident_to_cef(incident)
    for receiver in syslog_receivers:
        if not receiver.receiver.export_status:
            continue
        socktype = protocol_socktype_map.get(receiver.protocol, socket.SOCK_DGRAM)
        try:
            handler = logging.handlers.SysLogHandler(address=(receiver.host, receiver.port), socktype=socktype)
            my_logger.addHandler(handler)
            my_logger.info(cef_message)
            my_logger.removeHandler(handler)
        except Exception as e:
            _log.error(
                f"Following error occurred during event export to syslog host {receiver.host}:{receiver.port} [{e}]")


def export_incident_opcua(incident):
    """ Function for sending event (incident for now) to opcua receivers
    :param incident: Incident object for sending
    """
    opcua_receivers = OpcuaReceiver.objects.all()
    cef_message = incident_to_cef(incident)

    for receiver in opcua_receivers:
        if receiver.receiver.export_status:
            host_address = 'opc.tcp://' + str(receiver.host) + ':' + str(receiver.port)
            client = Client(host_address)
            try:
                client.connect()
                server_objects = client.get_objects_node()
                server_childs = server_objects.get_children()
                last_inc = server_childs[1]
                last_inc_value = last_inc.get_children()[0]
                last_inc_value.set_value(cef_message)
                client.disconnect()
            except Exception as e:
                _log.error(
                    f"Following error occurred during event export to opcua host {receiver.host}:{receiver.port} : [{e}]")


@shared_task
def export_incident(incident_pk):
    """ Task for exporting incidents
    @param incident_pk: PK Incident instance, which will be exported
    """
    try:
        incident_instance = Incident.objects.get(pk=incident_pk)
    except Incident.DoesNotExist:
        _log.warning(f'Incident with id {incident_pk} not found')
        return
    export_incident_syslog(incident_instance)
    export_incident_opcua(incident_instance)
