# -*- coding: utf-8 -*-
"""
/***************************************************************************
 PluginApiCredentialsDialog
 This plugin connects to the NDFF api
                             -------------------
        begin                : 2021-12-20
        git sha              : $Format:%H$
        copyright            : (C) 2021 by Zuidt
        email                : richard@zuidt.nl
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os

from qgis.PyQt import uic
from qgis.PyQt import QtWidgets
from qgis.PyQt.QtWidgets import (
    QDialog,
)
from qgis.PyQt.QtCore import (
    QSettings,
    QRegExp,
)
from qgis.PyQt.QtGui import (
     QRegExpValidator,
)
from .ext.ndff.connector import NdffConnector
from .ext.ndff.api import Api, NdffApiException

import logging
from . import LOGGER_NAME
log = logging.getLogger(LOGGER_NAME)

# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'ndffc_plugin_api_credentials_dialog_base.ui'))


class PluginApiCredentialsDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, connector: NdffConnector, parent=None):
        """Constructor."""
        super(PluginApiCredentialsDialog, self).__init__(parent)
        log.debug('PluginApiCredentialsDialog contructor')
        self.setupUi(self)

        self.cmb_api_environment.addItem('Acceptatie Omgeving: https://accapi.ndff.nl', userData=Api.ACC_ENVIRONMENT)
        self.cmb_api_environment.addItem('PRODUCTIE Omgeving: https://api.ndff.nl', userData=Api.PRD_ENVIRONMENT)
        self.cmb_api_environment.currentIndexChanged.connect(self.api_environment_changed)

        self.connector = connector

        valid_title_regexp = QRegExp('[-\\w]{6,}')
        self.le_title.setValidator(QRegExpValidator(valid_title_regexp))

        # fill credential settings combo with available settings sets (from QSettings)
        credential_settings = QSettings()
        credential_settings.beginGroup('ndffc_plugin/credentials')
        credential_sets = credential_settings.childGroups()
        credential_settings.endGroup()
        current = ''  # will be the one that is shown in the dialog
        # remove 'current' IF available
        if 'current' in credential_sets:
            credential_sets.remove('current')
            current = 'current'
        elif len(credential_sets) > 0:
            # IF we have other credential sets: pick the first one to show
            current = credential_sets[0]
        self.cb_credential_sets.addItems(credential_sets)

        # we are in QGIS, so taking /ndffc_plugin/credentials sets from QSettings,
        # and put all those in the cb_credential_sets
        # AND take the /ndffc_plugin/credentials/current set to put
        self.merge_api_settings_from_qsettings_into_connector(self.connector, set_name=current)
        self.fill_fields()

        self.btn_test_api.clicked.connect(self.test_api)
        self.cb_credential_sets.currentTextChanged.connect(self.credential_set_changed)

        # look if the level (in __init__.py is set to DEBUG/10, then set the checkbox to CHECKED
        self.cb_plugin_logs.setChecked(logging.getLogger('ndff_connector_plugin').level <= 10)
        # https://www.pythonguis.com/tutorials/transmitting-extra-data-qt-signals/
        self.cb_plugin_logs.stateChanged.connect(lambda checked: self.show_logs(checked, 'ndff_connector_plugin'))

        self.cb_api_logs.setChecked(logging.getLogger('ndff_connector_plugin.ext.ndff.api').level <= 10)
        self.cb_api_logs.stateChanged.connect(lambda checked: self.show_logs(checked, 'ndff_connector_plugin.ext.ndff.api'))

        self.cb_connector_logs.setChecked(logging.getLogger('ndff_connector_plugin.ext.ndff.connector').level <= 10)
        self.cb_connector_logs.stateChanged.connect(lambda checked: self.show_logs(checked, 'ndff_connector_plugin.ext.ndff.connector'))

        # ssl_validation setting is either True, 1 or 2 (int or string):
        if QSettings().value(f"/ndffc_plugin/ssl_validation", True) in (True, 1, '1', 2, '2'):
            self.cb_ssl_validation.setChecked(True)
        else:  # 0, False or something else?
            self.cb_ssl_validation.setChecked(False)
        self.cb_ssl_validation.stateChanged.connect(self.ssl_validation_changed)

        self.cb_non_modal_dialog.setChecked(QSettings().value(f"/ndffc_plugin/main_window_modality", False))
        self.cb_non_modal_dialog.stateChanged.connect(self.main_window_modality_changed)

    @staticmethod
    def show_logs(checked, source):
        # Qt.Unchecked == 0
        # Qt.Checked == 2
        if checked:
            logging.getLogger(source).setLevel(logging.DEBUG)
        else:  # unchecked
            logging.getLogger(source).setLevel(logging.WARNING)

    def api_environment_changed(self, index):
        log.debug(f'Environment changed to: {index} = {self.cmb_api_environment.itemData(index)}')

    def ssl_validation_changed(self, checked):
        log.debug(f'SSL validation changed to: {checked}')
        # key of ssl_validation will be saved in QSettings and picked up during
        QSettings().setValue(f"/ndffc_plugin/ssl_validation", checked)
        self.merge_api_settings_from_qsettings_into_connector(self.connector, set_name='current')
        self.fill_fields()

    def main_window_modality_changed(self, checked):
        log.debug(f'Window modality changed to {checked}')
        if checked:
            QSettings().setValue(f"/ndffc_plugin/main_window_modality", True)
        else:
            QSettings().setValue(f"/ndffc_plugin/main_window_modality", False)

    def credential_set_changed(self, set_name):
        """
        User changed the credential set in the combobox...
        Make sure all fields are properly shown
        """
        self.merge_api_settings_from_qsettings_into_connector(self.connector, set_name)
        self.fill_fields()

    @staticmethod
    def merge_api_settings_from_qsettings_into_connector(connector: NdffConnector, set_name='current'):
        # Below I thought to pick them up from QSettings, and IF they are present these
        title = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/title', False)
        if title:
            connector.ndff_api_settings['title'] = title.strip()  # just to be sure user did not put spaces on either end
        api_environment = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/api_environment', False)
        if api_environment:
            connector.ndff_api_settings['api_environment'] = api_environment.strip()  # just to be sure user did not put spaces on either end
        user = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/user', False)
        if user:
            connector.ndff_api_settings['user'] = user.strip()  # just to be sure user did not put spaces on either end
        password = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/password', False)
        if password:
            connector.ndff_api_settings['password'] = password.strip()  # just to be sure user did not put spaces on either end
        client_id = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/client_id', False)
        if client_id:
            connector.ndff_api_settings['client_id'] = client_id.strip()  # just to be sure user did not put spaces on either end
        client_secret = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/client_secret', False)
        if client_secret:
            connector.ndff_api_settings['client_secret'] = client_secret.strip()  # just to be sure user did not put spaces on either end
        domain = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/domain', False)
        if domain:
            connector.ndff_api_settings['domain'] = domain.strip()  # just to be sure user did not put spaces on either end
        domain_key = QSettings().value(f'/ndffc_plugin/credentials/{set_name}/domain_key', False)
        if domain_key:
            connector.ndff_api_settings['domain_key'] = domain_key.strip()  # just to be sure user did not put spaces on either end
        else:  # domain_key is optional, so: if not in config 'remove' it by setting it to None
            connector.ndff_api_settings['domain_key'] = None
        # it is possible to disable ssl validation of the http client
        validate_ssl = QSettings().value(f'/ndffc_plugin/ssl_validation', True)
        if validate_ssl:
            log.debug('DEFAULT SSL SETTINGS, OK')
            connector.ndff_api_settings['verify_ssl_certificate'] = True
        else:
            log.info('!!!! WAARSCHUWING: DISABLED SSL SETTINGS, ALLEEN GEBRUIKEN INDIEN NODIG !!!!!')
            connector.ndff_api_settings['verify_ssl_certificate'] = False

        if api_environment and user and password and client_id and client_secret and domain:
            return True
        else:
            # let's make sure NOTHING is actually set
            # Mmm then we are overriding eventually available settings from ndff settings files
            #connector.ndff_api_settings = {}
            return False

    def fill_fields(self):
        # Mmm, better to always fill the dialog inputs with what is available in the connector (probably from setting files if QSettings was empty)
        # now fill the fields in the dialog with these value
        if 'title' in self.connector.ndff_api_settings:
            self.le_title.setText(self.connector.ndff_api_settings['title'].strip())
            self.cb_credential_sets.setCurrentText(self.connector.ndff_api_settings['title'].strip())
        if 'api_environment' in self.connector.ndff_api_settings:
            if self.connector.ndff_api_settings['api_environment'] == Api.PRD_ENVIRONMENT:
                self.cmb_api_environment.setCurrentIndex(1)
            else:
                self.cmb_api_environment.setCurrentIndex(0)
        if 'user' in self.connector.ndff_api_settings:
            self.le_api_username.setText(self.connector.ndff_api_settings['user'])
        if 'password' in self.connector.ndff_api_settings:
            self.le_api_password.setText(self.connector.ndff_api_settings['password'])
        if 'client_id' in self.connector.ndff_api_settings:
            self.le_api_client_id.setText(self.connector.ndff_api_settings['client_id'])
        if 'client_secret' in self.connector.ndff_api_settings:
            self.le_api_client_secret.setText(self.connector.ndff_api_settings['client_secret'])
        if 'domain' in self.connector.ndff_api_settings:
            self.le_domain.setText(self.connector.ndff_api_settings['domain'])
        if 'domain_key' in self.connector.ndff_api_settings:
            self.le_domain_key.setText(self.connector.ndff_api_settings['domain_key'])

    def test_api(self):
        self.lbl_test_result.setText('...')
        try:
            if self.le_title.text() in ('None', '', None) or len(self.le_title.text()) < 6:
                raise NdffApiException('De titel is niet geldig: een korte tekst met minstens 6 karakters!')
            self.connector.ndff_api_settings['title'] = self.le_title.text().strip()  # just to be sure user did not put spaces on either end
            self.connector.ndff_api_settings['api_environment'] = self.cmb_api_environment.currentData()
            self.connector.ndff_api_settings['user'] = self.le_api_username.text().strip()  # just to be sure user did not put spaces on either end
            self.connector.ndff_api_settings['password'] = self.le_api_password.text().strip()  # just to be sure user did not put spaces on either end
            self.connector.ndff_api_settings['client_id'] = self.le_api_client_id.text().strip()  # just to be sure user did not put spaces on either end
            self.connector.ndff_api_settings['client_secret'] = self.le_api_client_secret.text().strip()  # just to be sure user did not put spaces on either end
            self.connector.ndff_api_settings['domain'] = self.le_domain.text().strip()  # just to be sure user did not put spaces on either end
            self.connector.ndff_api_settings['domain_key'] = self.le_domain_key.text().strip()  # just to be sure user did not put spaces on either end
            Api.remove_saved_tokens()  # just to be sure we do not use old tokens, which in combi with the domain will raise error
            api = self.connector.get_api(fresh_one=True)  # really need to get a fresh one when credentials/input has been changed !!!
            api.get_new_tokens()
            api.test_connection()
            user = self.connector.ndff_api_settings['user']
            acc_or_prd = 'PRODUKTIE'
            if 'api_environment' in self.connector.ndff_api_settings and Api.ACC_ENVIRONMENT in self.connector.ndff_api_settings['api_environment'].upper():
                acc_or_prd = 'ACCEPTATIE'
            log.debug(f'Connected with NDFF user:"{user}" environment: ({acc_or_prd})')
            self.lbl_test_result.setText(f'Succesvolle verbinding als "{user}" met de NDFF ({acc_or_prd}) API')
            return True
        except NdffApiException as ndffe:
            log.debug(f'Error connecting to the api: {ndffe}')
            self.lbl_test_result.setText(f'FOUT bij het maken van een verbinding met de NDFF (met deze gegevens)! Kijk in de log messages.\n{str(ndffe)[0:100]}...')
        except Exception as e:
            self.lbl_test_result.setText(f'Er heeft zich een (netwerk?) probleem voorgedaan:\n{e}')
            log.debug(f'There was a (network?) error:\n{e}')
            return -1

    def accept(self) -> None:
        log.debug(f'OK, clicked. Token for: "{self.le_api_username.text()}" domain {self.le_domain.text()}')
        if self.test_api():
            # NOW save as THE CURRENT set of values to the QSettings of the user
            # AND save as a set in a group named after the name of this set
            for credential_set_name in [self.le_title.text(), 'current']:
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/title", self.le_title.text())
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/api_environment", self.cmb_api_environment.currentData())
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/user", self.le_api_username.text())
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/password", self.le_api_password.text())
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/client_id", self.le_api_client_id.text())
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/client_secret", self.le_api_client_secret.text())
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/domain", self.le_domain.text())
                QSettings().setValue(f"/ndffc_plugin/credentials/{credential_set_name}/domain_key", self.le_domain_key.text())
            self.done(QDialog.Accepted)

    def reject(self) -> None:
        self.done(QDialog.Rejected)
