# Copyright (C) 2016-2022 Lightbits Labs Ltd.
# Copyright (C) 2020 Intel Corporation
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import glob
import http.client
import os
import re
import tempfile
import time
import traceback

from oslo_concurrency import lockutils
from oslo_concurrency import processutils as putils
from oslo_log import log as logging

from os_brick import exception
from os_brick.i18n import _
from os_brick.initiator.connectors import base
from os_brick.privileged import lightos as priv_lightos
from os_brick import utils


DEVICE_SCAN_ATTEMPTS_DEFAULT = 5
DISCOVERY_CLIENT_PORT = 6060
LOG = logging.getLogger(__name__)

synchronized = lockutils.synchronized_with_prefix('os-brick-')
nvmec_pattern = ".*nvme[0-9]+[cp][0-9]+.*"
nvmec_match = re.compile(nvmec_pattern)


class LightOSConnector(base.BaseLinuxConnector):
    """Connector class to attach/detach LightOS volumes using NVMe/TCP."""

    WAIT_DEVICE_TIMEOUT = 60

    def __init__(self,
                 root_helper,
                 driver=None,
                 execute=None,
                 device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
                 message_queue=None,
                 *args,
                 **kwargs):
        super(LightOSConnector, self).__init__(
            root_helper,
            driver=driver,
            execute=execute,
            device_scan_attempts=device_scan_attempts,
            *args, **kwargs)
        self.message_queue = message_queue
        self.DISCOVERY_DIR_PATH = '/etc/discovery-client/discovery.d/'

    @staticmethod
    def get_connector_properties(root_helper, *args, **kwargs):
        """The LightOS connector properties."""
        props = {}
        lightos_connector = LightOSConnector(root_helper=root_helper,
                                             message_queue=None,
                                             execute=kwargs.get('execute'))
        hostnqn = utils.get_host_nqn()
        found_dsc = lightos_connector.find_dsc()

        if not found_dsc:
            LOG.debug('LIGHTOS: did not find dsc, continuing anyway.')

        if hostnqn:
            LOG.debug("LIGHTOS: finally hostnqn: %s dsc: %s",
                      hostnqn, found_dsc)
            props['nqn'] = hostnqn
            props['found_dsc'] = found_dsc
        else:
            LOG.debug('LIGHTOS: no hostnqn found.')

        return props

    def dsc_file_name(self, uuid):
        return os.path.join(self.DISCOVERY_DIR_PATH, "%s.conf" % uuid)

    def find_dsc(self):
        conn = http.client.HTTPConnection("localhost", DISCOVERY_CLIENT_PORT)
        try:
            conn.request("HEAD", "/metrics")
            resp = conn.getresponse()
            return 'found' if resp.status == http.client.OK else ''
        except Exception as e:
            LOG.debug(f'LIGHTOS: {e}')
            out = ''
        return out

    def dsc_need_connect(self, connection_info):
        return not os.path.isfile(self.dsc_file_name(connection_info['uuid']))

    def dsc_connect_volume(self, connection_info):
        if not self.dsc_need_connect(connection_info):
            return

        subsysnqn = connection_info['subsysnqn']
        uuid = connection_info['uuid']
        hostnqn = utils.get_host_nqn()
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as dscfile:
            dscfile.write('# os_brick connector dsc file for LightOS'
                          ' volume: {}\n'.format(uuid))
            for (ip, node) in connection_info['lightos_nodes'].items():
                transport = node['transport_type']
                host = node['target_portal']
                port = node['target_port']
                dscfile.write('-t {} -a {} -s {} -q {} -n {}\n'.format(
                    transport, host, port, hostnqn, subsysnqn))
            dscfile.flush()
            try:
                dest_name = self.dsc_file_name(uuid)
                priv_lightos.move_dsc_file(dscfile.name, dest_name)
            except Exception:
                LOG.warning(
                    "LIGHTOS: Failed to create dsc file for connection with"
                    f" uuid:{uuid}")