# -*- coding: utf-8 -*-
"""
/***************************************************************************
 Geokodowanie adresów UUG GUGiK
                                 A QGIS plugin
 Wtyczka geokoduje adresy z pliku do warstwy punktowej
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2024-12-13
        git sha              : $Format:%H$
        copyright            : (C) 2024 by EnviroSolutions Sp. z o.o.
        email                : office@envirosolutions.pl
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""

from qgis.PyQt.QtCore import (
    QSettings, QTranslator, QCoreApplication, QEventLoop, QTimer, QUrl
)
from qgis.PyQt.QtGui import QIcon, QPixmap
from qgis.PyQt.QtNetwork import QNetworkRequest
from qgis.PyQt.QtWidgets import (
    QAction, QToolBar,
    QDialog
)
from qgis.core import (
    Qgis, QgsApplication, QgsVectorLayer, QgsProject, 
    QgsNetworkAccessManager, QgsSettings
)

from . import encoding
from os import path
import re
import os
from .resources import *
from .geokodowanie_adresow_dialog import GeokodowanieAdresowDialog
from .qgis_feed import QgisFeedDialog
from .geokoder import Geokodowanie

from .constants import REP, GUGIK, EPSG
from . import PLUGIN_NAME, PLUGIN_VERSION
from .utils import NetworkTools, NotifyTools

NetworkTools.patchQtCompatibility()

class GeokodowanieAdresow:

    def __init__(self, iface, is_tested=False):
        self.settings = QgsSettings() 
        self.iface = iface
        self.network_manager = QgsNetworkAccessManager.instance()
        self.notify_tools = NotifyTools(self.iface)
        self.network_toolkit = NetworkTools()
        self.plugin_dir = path.dirname(__file__)

        if not is_tested: 
            if Qgis.QGIS_VERSION_INT >= 31000:
                from .qgis_feed import QgisFeed
                self.selected_industry = self.settings.value("selected_industry", 
                    None)
                show_dialog = self.settings.value("showDialog", True, type=bool)

                if self.selected_industry is None and show_dialog:
                    self.showBranchSelectionDialog()

                select_indust_session = self.settings.value('selected_industry')

                self.feed = QgisFeed(
                    self,
                    selected_industry=select_indust_session, 
                    plugin_name=PLUGIN_NAME
                )
                self.feed.initFeed()

        
            locale = QSettings().value('locale/userLocale')[0:2]
            locale_path = path.join(
                self.plugin_dir,
                'i18n',
                'GeokodowanieAdresow_{}.qm'.format(locale))

            if path.exists(locale_path):
                self.translator = QTranslator()
                self.translator.load(locale_path)
                QCoreApplication.installTranslator(self.translator)
            
            # Declare instance attributes
            self.actions = []
            self.menu = self.tr(u'&EnviroSolutions')
            self.toolbar = self.iface.mainWindow().findChild(QToolBar, 
                'EnviroSolutions')

            if not self.toolbar:
                self.toolbar = self.iface.addToolBar(u'EnviroSolutions')
                self.toolbar.setObjectName(u'EnviroSolutions')

        self.first_start = None
        self.taskManager = QgsApplication.taskManager()
        self.project = QgsProject.instance()

    def tr(self, message):
        """Pobierz tłumaczenie dla ciągu znaków za pomocą interfejsu 
        tłumaczenia Qt.

        Implementujemy to samodzielnie, ponieważ nie dziedziczymyQObject.

        :param message: Ciąg znaków do tłumaczenia.
        :type message: str, QString

        :returns: Przetłumaczona wersja wiadomości.
        :rtype: QString
        """
        return QCoreApplication.translate('GeokodowanieAdresow', message)

    def addAction(
            self,
            icon_path,
            text,
            callback,
            enabled_flag=True,
            add_to_menu=True,
            add_to_toolbar=True,
            status_tip=None,
            whats_this=None,
            parent=None
        ):
        """Dodaj ikonę narzędzi do paska narzędzi.

        :param icon_path: Ścieżka do ikony dla tej akcji. Może być ścieżką 
        zasobu (np. ':/plugins/foo/bar.png') lub normalną ścieżką systemową.
        :type icon_path: str

        :param text: Tekst, który powinien być wyświetlany w elementach menu 
        dla tej akcji.
        :type text: str

        :param callback: Funkcja, która ma być wywołana, gdy akcja jest 
        wywołana.
        :type callback: function

        :param enabled_flag: Flaga wskazująca, czy akcja powinna być domyślnie 
        włączona. Domyślnie ustawione na True.
        :type enabled_flag: bool

        :param add_to_menu: Flaga wskazująca, czy akcja powinna być również
            dodana do menu. Domyślnie ustawione na True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flaga wskazująca, czy akcja powinna być również
            dodana do paska narzędzi. Domyślnie ustawione na True.
        :type add_to_toolbar: bool

        :param status_tip: Opis opcjonalny, który jest wyświetlany w oknie
            podręcznym, gdy wskaźnik myszy przechodzi przez akcję.
        :type status_tip: str

        :param parent: Widget nadrzędny dla nowej akcji. Domyślnie ustawione
            na None.
        :type parent: QWidget

        :param whats_this: Opis opcjonalny, który jest wyświetlany w status 
        barze gdy wskaźnik myszy przechodzi przez akcję.

        :returns: Akcja, która została utworzona. Uwaga, że akcja jest również
            dodana do listy self.actions.
        :rtype: QAction
        """
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)
        if whats_this is not None:
            action.setWhatsThis(whats_this)
        if add_to_toolbar:
            self.toolbar.addAction(action)
        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)
        return action
   
    def initGui(self):
        self.dlg = GeokodowanieAdresowDialog()
        """
        Tworzy elementy menu i ikony paska narzędzi wewnątrz interfejsu QGIS.
        """
        self.dlg.qfwOutputFile.setFilter(filter="Pliki tekstowe (*.txt)")
        self.dlg.qfwInputFile.setFilter(filter="Pliki CSV (*.csv)")
        icon_path = os.path.join(self.plugin_dir, 'images', 'icon_uug.svg')
        self.addAction(
            icon_path,
            text=self.tr(PLUGIN_NAME),
            callback=self.run,
            parent=self.iface.mainWindow())
        self.first_start = True
        self.notify_tools.pushLogInfo(f"Wtyczka {PLUGIN_NAME} została zainicjalizowana poprawnie")

    def unload(self):
        """Usuwa elementy menu i ikony paska narzędzi z interfejsu QGIS."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&EnviroSolutions'),
                action)
            self.toolbar.removeAction(action)

    def run(self):
        """
        Metoda run, która jest wywoływana, gdy użytkownik kliknie 
        na ikonę w pasku narzędzi.
        """

        if self.first_start == True:
            self.first_start = False
            self.dlg.qfwInputFile.fileChanged.connect(self.openInputFile)
            self.dlg.qfwOutputFile.fileChanged.connect(self.saveOutputFile)
            self.dlg.btnGeokoduj.clicked.connect(self.parseCsv)
            self.dlg.cbxDelimiter.currentTextChanged.connect(
                self.ledSymbolChanged
                )
            self.dlg.cbxDelimiter.activated.connect(self.readHeader)
            self.isInputFile = False
            self.isOutputFile = False
            self.dlg.cbxEncoding.addItems(encoding.encodings)
            utf8Id = self.dlg.cbxEncoding.findText('utf_8')
            self.dlg.cbxEncoding.setCurrentIndex(utf8Id)
            self.dlg.cbxEncoding.currentIndexChanged.connect(self.readHeader)
            self.ledSymbolChanged()
            self.dlg.img_main.setPixmap(
                QPixmap(os.path.join(
                    self.plugin_dir,
                    'images',
                    'icon_uug.svg'
                ))
            )
            self.dlg.setWindowTitle('%s %s' % (PLUGIN_NAME, PLUGIN_VERSION))
            self.dlg.lbl_pluginVersion.setText('%s %s' % (PLUGIN_NAME, PLUGIN_VERSION))
        
        connection = self.checkInternetConnection()
        if not connection:
            msg = f"Brak połączenia z internetem lub usługa GUGiK jest niedostępna."
            self.notify_tools.pushWarning(msg) 
            self.notify_tools.pushLogWarning(msg)
            return

        self.taskManager.cancelAll()
        self.dlg.show()
        result = self.dlg.exec()

        if result:
            pass

    def showBranchSelectionDialog(self):
        self.qgisfeed_dialog = QgisFeedDialog()

        if self.qgisfeed_dialog.exec() == QDialog.Accepted:
            self.selected_branch = self.qgisfeed_dialog.comboBox.currentText()
            self.settings.setValue("selected_industry", self.selected_branch, 
                QgsSettings.NoSection)
            self.settings.setValue("showDialog", False, QgsSettings.NoSection)
            self.settings.sync()

    def openInputFile(self):
        """
        Otwiera okno dialogowe do wyboru pliku CSV.
        Po wybraniu pliku aktualizuje interfejs użytkownika.
        """
        self.inputPlik = self.dlg.qfwInputFile.filePath()
        
        # Sprawdza, czy użytkownik wybrał plik
        if self.inputPlik != '':
            self.isInputFile = True 
            if self.isInputFile and self.isOutputFile:
                self.dlg.btnGeokoduj.setEnabled(True)
            self.readHeader()

    def saveOutputFile(self):
        """
        Otwiera okno dialogowe do zapisu pliku tekstowego.
        Po wybraniu miejsca zapisu aktualizuje interfejs użytkownika.
        """
        self.outputPlik = self.dlg.qfwOutputFile.filePath()
        
        # Sprawdza, czy użytkownik wybrał miejsce zapisu pliku
        if self.outputPlik != '':
            self.isOutputFile = True
            if self.isInputFile and self.isOutputFile:
                self.dlg.btnGeokoduj.setEnabled(True)

    def readHeader(self):
        """
        Czyści ComboBoxy w interfejsie użytkownika.

        Sprawdza, czy plik wejściowy został wybrany. 
        Jeśli tak, otwiera plik i odczytuje pierwszą linię (nagłówek).
        Jeśli wystąpi błąd UnicodeDecodeError podczas odczytu pliku, 
        wyświetla komunikat ostrzegawczy i przerywa działanie funkcji.
        W przeciwnym razie odczytuje elementy nagłówka, rozdzielając 
        je na listę elementów za pomocą określonego separatora (delimeter).
        Następnie dodaje elementy nagłówka do odpowiednich ComboBoxów 
        w interfejsie użytkownika.
        """
        self.dlg.cbxMiejscowosc.clear()
        self.dlg.cbxUlica.clear()
        self.dlg.cbxNumer.clear()
        self.dlg.cbxKod.clear()

        if self.isInputFile:
            with open(self.inputPlik, 'r', 
                encoding=self.dlg.cbxEncoding.currentText()) as plik:
                try:
                    naglowki = plik.readline()
                except UnicodeDecodeError:
                    msg = (
                        "Błąd kodowania: Nie udało się zastosować wybranego"
                        " kodowania do pliku z adresami. Spróbuj innego kodowania."
                    )
                    self.notify_tools.pushWarning(msg)
                    self.notify_tools.pushLogWarning(msg)
                    return False

                elementyNaglowkow = naglowki.split(self.delimeter)
                elementyNaglowkow = [x.strip() for x in elementyNaglowkow]
                elementyNaglowkow.insert(0, "")
                self.dlg.cbxMiejscowosc.addItems(elementyNaglowkow)
                self.dlg.cbxUlica.addItems(elementyNaglowkow)
                self.dlg.cbxNumer.addItems(elementyNaglowkow)
                self.dlg.cbxKod.addItems(elementyNaglowkow)

                self.notify_tools.pushLogInfo(
                    f"Czytanie nagłówków: {elementyNaglowkow}"
                )
                
    def csvCheck(
            self, 
            rekordy, 
            idMiejscowosc, 
            idUlica, 
            idNumer, 
            idKod
        ):
        """Sprawdzenie poprawności CSV"""
        for rekord in rekordy:
            wartosci = rekord.strip().split(self.delimeter)
            try:
                if idMiejscowosc:
                    wartosci[idMiejscowosc - 1]
                if idUlica:
                    wartosci[idUlica - 1]
                if idNumer:
                    wartosci[idNumer - 1]
                if idKod:
                    wartosci[idKod - 1]

            except IndexError:
                msg = (
                    "Błąd wczytywania pliku: błąd w wierszu nr"
                    f"{rekordy.index(rekord)}: {rekord}"
                )
                self.notify_tools.pushCritical(msg)
                return False
        return True

    def createEmptyLayer(
            self, 
            headings, 
            hasHeadings=True
        ):
        """
        Tworzy pustą warstwę wektorową.

        Tworzy ciąg pól warstwy na podstawie nagłówków lub indeksów.
        Tworzy obiekt warstwy wektorowej punktów w pamięci.
        Zwraca obiekt warstwy wektorowej.

        :param headings: Lista nagłówków lub indeksów pól.
        :param hasHeadings: Określa, czy nagłówki są dostępne 
        (domyślnie True).
        :return: Obiekt warstwy wektorowej.
        """
        fields = ''
        if hasHeadings:
            for heading in headings:
                fields += "&field=%s:string(0,-1)" % heading
        else:
            for i in range(len(headings)):
                fields += "&field=pole%i:string(0,-1)" % (i + 1)

        warstwaPoint = QgsVectorLayer(
            f"Point?crs=EPSG:{EPSG}" + fields
            , "zgeokodowane lokalizacje", "memory")
        warstwaLine = QgsVectorLayer(
            f"LineString?crs=EPSG:{EPSG}" + fields
            , "zgeokodowane ulice", "memory")
        warstwaPoly = QgsVectorLayer(
            f"Polygon?crs=EPSG:{EPSG}" + fields
            , "zgeokodowane place", "memory")

        return warstwaPoint, warstwaLine, warstwaPoly


    def parseCsv(self):
        """
        Parsuje plik CSV, przetwarza jego zawartość i inicjuje proces 
        geokodowania.

        Sprawdza, czy wybrane atrybuty są poprawnie wybrane, czy plik CSV
        jest poprawny, a następnie przetwarza rekordy, tworzy listy wartości
        dla poszczególnych atrybutów i inicjuje proces geokodowania.
        """
        # testowo
        self.inputPlik = self.dlg.qfwInputFile.filePath()
        self.outputPlik = self.dlg.qfwOutputFile.filePath()
        msg = (
            f"Plik wejściowy {self.inputPlik},"
            f" Plik wyjściowy {self.outputPlik}."
        )
        self.notify_tools.pushLogInfo(msg)
        #
        idMiejscowosc = self.dlg.cbxMiejscowosc.currentIndex()
        idUlica = self.dlg.cbxUlica.currentIndex()
        idNumer = self.dlg.cbxNumer.currentIndex()
        idKod = self.dlg.cbxKod.currentIndex()

        msg = (
            f"Rozpoczyna parsowanie pliku CSV. Kolumny uwzględniane: - "
            f"Miasto: {idMiejscowosc}, Ulica: {idUlica}, "
            f"Numer: {idNumer}, Kod: {idKod}"
        )
        self.notify_tools.pushLogInfo(msg)

        # Sprawdza, czy co najmniej jeden atrybut jest wybrany
        if not idMiejscowosc and not idUlica and not idNumer and not idKod:
            # Informuje użytkownika, że nie wybrano żadnych atrybutów
            msg = "Nie wybrano żadnych atrybutów."
            self.notify_tools.pushWarning(msg)
            return
        # Sprawdza, czy wybrano miejscowość, jeśli nie, informuje użytkownika
        elif not idMiejscowosc:
            msg = f"Nie wybrano miejscowości."
            self.notify_tools.pushMessage(msg) 
        else:
            miejscowosci = []
            ulicy = []
            numery = []
            kody = []

            with open(
                self.inputPlik, 'r', \
                encoding=self.dlg.cbxEncoding.currentText()
                ) as plik:
                
                try:
                    zawartosc = plik.readlines()
                except UnicodeDecodeError:
                    return False

                self.naglowek = zawartosc[0]
                naglowki = self.naglowek.strip().split(self.delimeter)

                if self.dlg.cbxFirstRow.isChecked():
                    self.rekordy = zawartosc[1:]
                    self.warstwaPoint, self.warstwaLine, self.warstwaPoly = \
                    self.createEmptyLayer(
                        headings=naglowki, 
                        hasHeadings=True
                    )
                else:
                    self.rekordy = zawartosc[:]
                    self.rekordy[0] = self.rekordy[0][1:]
                    self.warstwaPoint, self.warstwaLine, self.warstwaPoly = \
                    self.createEmptyLayer(
                        headings=naglowki, 
                        hasHeadings=False
                    )

                # sprawdzenie czy plik CSV jest poprawny
                if not self.csvCheck(self.rekordy, idMiejscowosc, idUlica, idNumer, idKod):
                    msg = "Plik CSV jest niepoprawny"
                    self.notify_tools.pushLogWarning(msg)
                    self.notify_tools.pushWarning(msg)
                    return False
                
                for rekord in self.rekordy:
                    try:
                        wartosci = rekord.strip().split(self.delimeter)
                        miejscowosci.append(
                            wartosci[idMiejscowosc - 1] 
                            if idMiejscowosc else ""
                        )
                        ulicy.append(
                            self.dealWithAbbreviations(wartosci[idUlica - 1])
                            if idUlica else ""
                        )
                        numery.append(
                            self.korektaFormatu(wartosci[idNumer - 1].upper())
                            if idNumer else ""
                        )
                        kody.append(
                            self.korektaFormatu(wartosci[idKod - 1])
                            if idKod else ""
                        )

                    except Exception as e:
                        msg  = f"Błąd przetwarzania rekordu {str(e)}"
                        self.notify_tools.pushCritical(msg)
                        self.notify_tools.pushLogCritical(msg)
                        continue

                # Tworzy obiekt geokodowania do menedżera zadań.
                task = Geokodowanie(
                    parent = self,
                    rekordy = self.rekordy, 
                    miejscowosci = miejscowosci, 
                    ulicy = ulicy, 
                    numery = numery, 
                    kody = kody,
                    delimeter = self.delimeter, 
                    iface = self.iface,
                )
                self.taskManager.addTask(task)
                self.dlg.btnGeokoduj.setEnabled(False)
            task.finishedProcessing.connect(self.geokodowanieSukces)
    

    def korektaFormatu(self, numer):
        """
        Korektuje format numeru.

        Sprawdza, czy numer zawiera cudzysłów.
        Jeśli tak, usuwa go.
        Zwraca skorygowany numer.
        """
        if '"' in numer:
            numer = numer.replace('"', '')
        return numer

      
    def dealWithAbbreviations(self, text):
        """
        Przetwarza skróty na pełne nazwy.

        Tworzy słownik przekształceń skrótów na pełne nazwy.
        Tworzy wyrażenie regularne z przekształconych skrótów.
        Zamienia skróty na pełne nazwy w tekście.
        Zamienia polskie cudzysłowy na standardowe cudzysłowy.
        Zwraca przekształcony tekst.
        """
        rep = REP
        rep = dict((re.escape(k), v) for k, v in rep.items())
        self.pattern = re.compile("|".join(rep.keys()))
        text = self.pattern.sub(lambda m: rep[re.escape(m.group(0))], text)
        if "„" in text or "”" in text:
            text = text.replace("„", '"').replace("”", '"')
        return text    

      
    def ledSymbolChanged(self):
        """
        Obsługuje zmianę symbolu separacji.

        Aktualizuje zmienną self.delimeter na podstawie wybranej opcji
        z listy rozwijanej w interfejsie użytkownika.
        """
        self.delimeter = self.dlg.cbxDelimiter.currentText()
        if self.delimeter == '':
            self.delimeter = ','
        if self.delimeter == "Spacja":
            self.delimeter = ' '

    def saveErrors(self, listaWierszy):
        """
        Zapisuje błędne adresy do pliku tekstowego.
        """
        with open(self.outputPlik, 'w') as plik: 
            plik.writelines(listaWierszy)

    def geokodowanieSukces(
                self, 
                featuresPoint, 
                featuresLine, 
                featuresPoly, 
                bledne, 
                stop
        ):
        """
        Funkcja przetwarza wyniki geokodowania, dodając je do odpowiednich warstw
        w QGIS, aktualizując te warstwy oraz wyświetlając odpowiednie komunikaty.

        Parametry:
            featuresPoint (list): Lista obiektów geometrii punktowej.
            featuresLine (list): Lista obiektów geometrii liniowej.
            featuresPoly (list): Lista obiektów geometrii poligonowej.
            bledne (list): Lista błędnych adresów, które nie zostały zgeokodowane.
            stop (bool): Flaga wskazująca, czy proces geokodowania został zatrzymany.
        """
        warstwy = [self.warstwaPoint, self.warstwaLine, self.warstwaPoly]
        self.dlg.btnGeokoduj.setEnabled(True)
        self.warstwaPoint.dataProvider().addFeatures(featuresPoint)
        self.warstwaLine.dataProvider().addFeatures(featuresLine)
        self.warstwaPoly.dataProvider().addFeatures(featuresPoly)
        
        for warstwa in warstwy:
            warstwa.updateExtents()
            if warstwa.featureCount() > 0:
                self.project.addMapLayer(warstwa)
        
        iloscZgeokodowanych = (
            len(featuresLine) 
            + len(featuresPoint) 
            + len(featuresPoly)
        )
        iloscRekordow = len(self.rekordy)
        
        self.notify_tools.pushLogInfo(
            f"Geokodowanie zakończone. Przetworzono: {iloscRekordow} rekordów. "
            f"Zgeokodowano: {iloscZgeokodowanych}."
        )
        
        # Wyświetlenie komunikatu, jeśli proces geokodowania został zatrzymany
        if stop:
            msg = (
                "Proces geokodowania został zatrzymany: "
                f"Zdekodowano {iloscZgeokodowanych}/{iloscRekordow} adresów."
                " Błędnie zgeokodowane adresy zostały zapisane w "
                f"pliku {self.outputPlik}."
            )
            self.notify_tools.pushMessage(msg)        
        # Wyświetlenie komunikatu, jeśli są błędne adresy lub żaden adres nie został zgeokodowany
        elif bledne or iloscZgeokodowanych == 0:
            iloscBledow = len(bledne)
            bledne.insert(
                0, "Miejscowość,Ulica,Numer Porządkowy,Kod Pocztowy \n"
            )
            self.saveErrors(bledne)
            msg = (
                "Wynik geokodowania: "
                f"Zgeokodowano {iloscZgeokodowanych}/{iloscRekordow}. "
                f"Pozostałe zostały zapisane w pliku {self.outputPlik}."
            )
            self.notify_tools.pushMessage(msg)
        
        # Wyświetlenie komunikatu, jeśli liczba zgeokodowanych adresów jest większa niż liczba rekordów
        elif iloscZgeokodowanych > iloscRekordow:
            msg = (
                "Wynik geokodowania: "
                f"Zgeokodowano {iloscZgeokodowanych}/{iloscRekordow}. "
                "Dla niektórych adresów usługa geokodowania zwróciła "
                "kilka wartości."
            )
            self.notify_tools.pushSuccess(msg)
        
        # Wyświetlenie komunikatu, jeśli wszystkie adresy zostały poprawnie zgeokodowane
        elif not bledne:
            msg = (
                "Wynik geokodowania:"
                f"Zgeokodowano wszystkie {iloscZgeokodowanych} adresów"
            )
            self.notify_tools.pushSuccess(msg)
      
    def checkInternetConnection(self):
        """
        Sprawdza, czy jest dostęp do internetu.
        
        Sprawdza, czy jest dostęp do internetu, próbując połączyć się z
        serwerem GUGIK. Jeśli jest możliwe, zwraca True, w przeciwnym
        razie zwraca False.
        
        :return: True, jeśli jest dostęp do internetu, False w przeciwnym
        razie.
        """
        try:
            request = QNetworkRequest(QUrl(GUGIK))

            request.setHeader(
                self.network_toolkit.getUAHeader(), 
                f"QGIS-Plugin-{PLUGIN_NAME}"
            )

            loop = QEventLoop()
            timer = QTimer()
            timer.setSingleShot(True)
            reply = self.network_manager.get(request)
            reply.finished.connect(loop.quit)
            timer.timeout.connect(loop.quit)
            timer.start(5000)
            loop.exec()
            
            if not timer.isActive():
                reply.abort()
                msg = "Nie otrzymano na czas odpowiedzi z GUGIK - Timeout."
                self.notify_tools.pushWarning(msg)
                self.notify_tools.pushLogWarning(msg)
                return False

            timer.stop()

            if reply.error() == self.network_toolkit.getNetworkNoError():
                status = reply.attribute(self.network_toolkit.getHttpStatusAttr())
                if status in (200, 301, 302):
                    return True
                msg = f"Nie połączono się z serwisem. Kod błędu: {status}"
                self.notify_tools.pushWarning(msg)
                self.notify_tools.pushLogWarning(msg)
            else:
                msg = f"Problem z połączeniem do serwera GUGIK. Błąd: {reply.errorString()}"
                self.notify_tools.pushCritical(msg)
                self.notify_tools.pushLogCritical(msg) 

            return False
        
        except Exception as e:
            msg = f"Nieznany problem z połączeniem: {str(e)}"
            self.notify_tools.pushCritical(msg)
            self.notify_tools.pushLogCritical(msg) 
            return False

