# -*- coding: utf-8 -*-
"""
/***************************************************************************
Power_EZiUDP
                                 A QGIS plugin
Power_EZiUDP
 Generated by Plugin Builder
 ***************************************************************************/
"""

from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication, Qt, QUrl, QStringListModel
from qgis.PyQt.QtGui import QIcon, QDesktopServices, QColor, QCursor
from qgis.PyQt import QtWidgets
from qgis.core import (
    QgsRasterLayer, QgsVectorLayer, QgsProject, QgsFeatureRequest, QgsGeometry,
    QgsCoordinateTransform, QgsCoordinateReferenceSystem, QgsRectangle, QgsFeature, QgsMessageLog, Qgis
)
from qgis.PyQt.QtWidgets import (QDialog, QVBoxLayout, QPushButton, 
                                QCompleter, QApplication, QToolButton, QMenu,
                                QTableWidget, QTableWidgetItem, QLineEdit,
                                QHeaderView, QAbstractItemView, QListWidget, QMessageBox,
                                QAction)
from qgis.gui import QgsMapToolEmitPoint, QgsRubberBand
import os.path, re, requests, sqlite3

from .resources import *
from .Power_EZiUDP_dockwidget import Power_EZiUDPDockwidget

import webbrowser


# ===============================
# DIALOG WYBORU WARSTW
# ===============================
# ===============================
# DIALOG WYBORU WARSTW (NOWA, ULEPSZONA WERSJA)
# ===============================
class LayerSelectionDialog(QDialog):
    def __init__(self, layers, service_type=None, parent=None):
        super().__init__(parent)
        self.service_type = service_type
        self.setWindowTitle("Wybierz warstwy do dodania")
        self.resize(700, 600)  # Zwiększamy rozmiar dla lepszej czytelności tabeli i nowych przycisków

        layout = QVBoxLayout(self)

        # 1. Pole wyszukiwania (filtrowania)
        self.search_box = QLineEdit()
        self.search_box.setPlaceholderText("Filtruj po nazwie lub tytule warstwy...")
        self.search_box.textChanged.connect(self.filter_table)
        layout.addWidget(self.search_box)

        # 1a. Przyciski zaznaczania
        selection_layout = QtWidgets.QHBoxLayout()
        self.btn_select_all = QPushButton("Zaznacz wszystko")
        self.btn_deselect_all = QPushButton("Odznacz wszystko")
        self.btn_invert = QPushButton("Odwróć zaznaczenie")
        
        self.btn_select_all.clicked.connect(self.select_all)
        self.btn_deselect_all.clicked.connect(self.deselect_all)
        self.btn_invert.clicked.connect(self.invert_selection)
        
        selection_layout.addWidget(self.btn_select_all)
        selection_layout.addWidget(self.btn_deselect_all)
        selection_layout.addWidget(self.btn_invert)
        layout.addLayout(selection_layout)

        # 2. Tabela zamiast listy
        self.table_widget = QTableWidget()
        self.table_widget.setColumnCount(3)
        self.table_widget.setHorizontalHeaderLabels(["", "Tytuł", "Nazwa (identyfikator)"])
        self.table_widget.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.table_widget.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
        self.table_widget.setSortingEnabled(True)
        
        # Ustawienie szerokości kolumn
        header = self.table_widget.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents) # Kolumna z checkboxem
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) # Tytuł rozciąga się
        header.setSectionResizeMode(2, QHeaderView.ResizeMode.Interactive) # Nazwę można ręcznie rozszerzyć

        # Wypełnienie tabeli danymi
        for name, title in layers:
            row_position = self.table_widget.rowCount()
            self.table_widget.insertRow(row_position)
            
            # Kolumna 0: Checkbox
            check_item = QTableWidgetItem()
            check_item.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
            check_item.setCheckState(Qt.CheckState.Unchecked)
            self.table_widget.setItem(row_position, 0, check_item)

            # Kolumna 1: Tytuł
            self.table_widget.setItem(row_position, 1, QTableWidgetItem(title))

            # Kolumna 2: Nazwa
            self.table_widget.setItem(row_position, 2, QTableWidgetItem(name))
            
        self.table_widget.resizeColumnToContents(2) # Dopasuj szerokość kolumny z nazwą
        layout.addWidget(self.table_widget)
        
        # 2a. Przycisk kopiowania z menu
        self.btn_copy = QtWidgets.QToolButton()
        self.btn_copy.setText("Opcje kopiowania...")
        self.btn_copy.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup)
        self.btn_copy.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
        
        copy_menu = QMenu(self)
        
        act_sel_titles = QAction("Kopiuj tytuły zaznaczonych", self)
        act_sel_titles.triggered.connect(self.copy_selected_titles)
        copy_menu.addAction(act_sel_titles)
        
        act_all_titles = QAction("Kopiuj tytuły wszystkich", self)
        act_all_titles.triggered.connect(self.copy_all_titles)
        copy_menu.addAction(act_all_titles)
        
        copy_menu.addSeparator()
        
        act_sel_names = QAction("Kopiuj nazwy zaznaczonych", self)
        act_sel_names.triggered.connect(self.copy_selected_names)
        copy_menu.addAction(act_sel_names)
        
        act_all_names = QAction("Kopiuj nazwy wszystkich", self)
        act_all_names.triggered.connect(self.copy_all_names)
        copy_menu.addAction(act_all_names)
        
        copy_menu.addSeparator()
        
        act_sel_names_csv = QAction("Kopiuj nazwy zazn. (bez przestrzeni, przecinek)", self)
        act_sel_names_csv.triggered.connect(self.copy_selected_names_csv_no_ns)
        copy_menu.addAction(act_sel_names_csv)
        
        act_all_names_csv = QAction("Kopiuj nazwy wsz. (bez przestrzeni, przecinek)", self)
        act_all_names_csv.triggered.connect(self.copy_all_names_csv_no_ns)
        copy_menu.addAction(act_all_names_csv)
        
        self.btn_copy.setMenu(copy_menu)
        layout.addWidget(self.btn_copy)

        # 2b. Opcja dla WFS - tylko zasięg widoku
        self.cb_extent_only = None
        if self.service_type == "WFS":
            self.cb_extent_only = QtWidgets.QCheckBox("Pobieraj tylko obiekty w zasięgu widoku")
            layout.addWidget(self.cb_extent_only)

        # 3. Przycisk
        self.btn_ok = QPushButton("Dodaj zaznaczone")
        self.btn_ok.clicked.connect(self.accept)
        layout.addWidget(self.btn_ok)

    def select_all(self):
        """Zaznacza wszystkie widoczne warstwy."""
        for i in range(self.table_widget.rowCount()):
            if not self.table_widget.isRowHidden(i):
                self.table_widget.item(i, 0).setCheckState(Qt.CheckState.Checked)

    def deselect_all(self):
        """Odznacza wszystkie widoczne warstwy."""
        for i in range(self.table_widget.rowCount()):
            if not self.table_widget.isRowHidden(i):
                self.table_widget.item(i, 0).setCheckState(Qt.CheckState.Unchecked)

    def invert_selection(self):
        """Odwraca zaznaczenie widocznych warstw."""
        for i in range(self.table_widget.rowCount()):
            if not self.table_widget.isRowHidden(i):
                item = self.table_widget.item(i, 0)
                if item.checkState() == Qt.CheckState.Checked:
                    item.setCheckState(Qt.CheckState.Unchecked)
                else:
                    item.setCheckState(Qt.CheckState.Checked)

    def copy_selected_titles(self):
        """Kopiuje tytuły zaznaczonych warstw do schowka."""
        titles = []
        for i in range(self.table_widget.rowCount()):
            if self.table_widget.item(i, 0).checkState() == Qt.CheckState.Checked:
                titles.append(self.table_widget.item(i, 1).text())
        copy_to_clipboard("\n".join(titles))

    def copy_all_titles(self):
        """Kopiuje tytuły wszystkich warstw do schowka."""
        titles = [self.table_widget.item(i, 1).text() for i in range(self.table_widget.rowCount())]
        copy_to_clipboard("\n".join(titles))

    def copy_selected_names(self):
        """Kopiuje nazwy zaznaczonych warstw do schowka."""
        names = []
        for i in range(self.table_widget.rowCount()):
            if self.table_widget.item(i, 0).checkState() == Qt.CheckState.Checked:
                names.append(self.table_widget.item(i, 2).text())
        copy_to_clipboard("\n".join(names))

    def copy_all_names(self):
        """Kopiuje nazwy wszystkich warstw do schowka."""
        names = [self.table_widget.item(i, 2).text() for i in range(self.table_widget.rowCount())]
        copy_to_clipboard("\n".join(names))

    def copy_selected_names_csv_no_ns(self):
        """Kopiuje zaznaczone nazwy bez przestrzeni nazw, oddzielone przecinkami."""
        names = []
        for i in range(self.table_widget.rowCount()):
            if self.table_widget.item(i, 0).checkState() == Qt.CheckState.Checked:
                name = self.table_widget.item(i, 2).text()
                if ":" in name:
                    name = name.split(":")[-1]
                names.append(name)
        copy_to_clipboard(",".join(names))

    def copy_all_names_csv_no_ns(self):
        """Kopiuje wszystkie nazwy bez przestrzeni nazw, oddzielone przecinkami."""
        names = []
        for i in range(self.table_widget.rowCount()):
            name = self.table_widget.item(i, 2).text()
            if ":" in name:
                name = name.split(":")[-1]
            names.append(name)
        copy_to_clipboard(",".join(names))

    def filter_table(self, text):
        """Filtruje wiersze w tabeli na podstawie wprowadzonego tekstu."""
        filter_text = text.lower()
        for row in range(self.table_widget.rowCount()):
            title_item = self.table_widget.item(row, 1)
            name_item = self.table_widget.item(row, 2)
            
            # Sprawdź, czy tekst filtra pasuje do tytułu lub nazwy
            matches = (filter_text in title_item.text().lower() or 
                       filter_text in name_item.text().lower())
            
            self.table_widget.setRowHidden(row, not matches)

    def get_selected(self):
        """Zwraca listę NAZW (identyfikatorów) zaznaczonych warstw."""
        selected_names = []
        for i in range(self.table_widget.rowCount()):
            item = self.table_widget.item(i, 0) # Item z checkboxem
            if item.checkState() == Qt.CheckState.Checked:
                # Pobierz nazwę warstwy z trzeciej kolumny (indeks 2)
                layer_name = self.table_widget.item(i, 2).text()
                selected_names.append(layer_name)
        return selected_names

    def is_extent_restricted(self):
        """Zwraca True, jeśli checkbox ograniczenia zasięgu jest zaznaczony."""
        if self.cb_extent_only:
            return self.cb_extent_only.isChecked()
        return False


# ===============================
# NARZĘDZIE KLIKNIĘCIA NA MAPIE
# ===============================
class PointTool(QgsMapToolEmitPoint):
    def __init__(self, iface, callback):
        super().__init__(iface.mapCanvas())
        self.iface = iface
        self.callback = callback

    def canvasReleaseEvent(self, event):
        point = self.toMapCoordinates(event.pos())
        self.callback(point)
        self.iface.mapCanvas().unsetMapTool(self)


# ===============================
# FUNKCJE OBSŁUGI GETCAPABILITIES
# ===============================
def fetch_capabilities(url: str):
    """Pobiera dokument GetCapabilities i zwraca listę nazw warstw oraz typ usługi."""
    try:
        if "SERVICE=WFS" in url.upper():
            service_type = "WFS"
        elif "SERVICE=WMS" in url.upper():
            service_type = "WMS"
        else:
            if "wfs" in url.lower():
                service_type = "WFS"
                url = url.split("?")[0]
                url += "?SERVICE=WFS&REQUEST=GetCapabilities"
            else:
                service_type = "WMS"
                url = url.split("?")[0]
                url += "?SERVICE=WMS&REQUEST=GetCapabilities"

        response = requests.get(url, timeout=10, verify=False)
        # response.raise_for_status()
        try:
            raw = response.content
            text = raw.decode('utf-8', errors='replace')
        except:
            text = response.text

        names = re.findall(r"<Name.*?>(.*?)</Name>", text)
        titles = re.findall(r"<Title.*?>(.*?)</Title>", text)
        if len(titles) > len(names):
            titles = titles[-len(names):]

        layers = list(zip(names, titles))
        return layers, service_type
    except Exception as e:
        print("Błąd pobierania GetCapabilities:", e)
        return [], url

def validate_service(url, teryt, baza, iface):

    if 'service=WFS' in url:
        typ = "wfs"
    else:
        typ= "wms"

    wzorzec = r'\((.*?)\)'
    dane= re.findall(wzorzec, baza)[0]

    url = url.split("?")[0]

    validate_url = f" https://walidator.gugik.gov.pl/service?url={url}&typ={typ}&dane={dane}&teryt={teryt}"
    QgsMessageLog.logMessage(f"Walidacja usługi: {validate_url}", "Power_EZiUDP", Qgis.Info)
    # iface.messageBar().pushWarning("EZiUDP", f"{validate_url}")
    webbrowser.open_new(validate_url)
    # QDesktopServices.openUrl(QUrl(validate_url))

def add_layers_from_service(url: str, iface):

        

    # url = url.split("?")[0] if "?" in url else url
    layers, service_type = fetch_capabilities(url)
    if not layers:
        iface.messageBar().pushWarning("EZiUDP", f"Nie udało się pobrać listy warstw z\nURL:{url}")
        return

    dlg = LayerSelectionDialog(layers, service_type)
    if dlg.exec():
        selected_layer_names = dlg.get_selected() # Teraz dostajemy czystą listę nazw
        restrict_extent = dlg.is_extent_restricted()
        if not selected_layer_names:
            iface.messageBar().pushInfo("EZiUDP", "Nie wybrano żadnych warstw.")
            return

        # Pętla jest teraz znacznie prostsza
        for layer_name in selected_layer_names:
            if service_type == "WMS":
                clean_url = re.sub(r"[?&]request=GetCapabilities", "", url, flags=re.I)
                clean_url = re.sub(r"[?&]service=WMS", "", clean_url, flags=re.I)
                base_url = clean_url.split("?")[0]
                layer_url = f"url={base_url}?service=WMS&request=GetMap&layers={layer_name}&styles=&format=image/png&crs=EPSG:2180"
                rlayer = QgsRasterLayer(layer_url, layer_name, "wms")
                if rlayer.isValid():
                    QgsProject.instance().addMapLayer(rlayer)
                else:
                    iface.messageBar().pushWarning("EZiUDP", f"Nie udało się dodać WMS: {layer_name}")

            elif service_type == "WFS":
                # Próba z wersją 2.0.0, a potem fallback na 1.1.0
                base_url = url.split('?')[0]
                
                # Budowanie URI dla WFS z uwzględnieniem ograniczenia zasięgu
                # W QGIS dla dostawcy WFS używamy formatu: url='...' typename='...'
                uri_params = [
                    f"url='{base_url}'",
                    f"typename='{layer_name}'"
                ]
                
                if restrict_extent:
                    uri_params.append("restrictToRequestBBOX='1'")
                
                # Próba z wersją 2.0.0
                uri_2_0 = " ".join(uri_params + ["version='2.0.0'"])
                vlayer = QgsVectorLayer(uri_2_0, layer_name, "WFS")
                
                if vlayer.isValid():
                    QgsProject.instance().addMapLayer(vlayer)
                else:
                    # Fallback na 1.1.0
                    uri_1_1 = " ".join(uri_params + ["version='1.1.0'"])
                    vlayer = QgsVectorLayer(uri_1_1, layer_name, "WFS")
                    if vlayer.isValid():
                        QgsProject.instance().addMapLayer(vlayer)
                    else:
                        # Ostatnia próba - auto
                        uri_auto = " ".join(uri_params + ["version='auto'"])
                        vlayer = QgsVectorLayer(uri_auto, layer_name, "WFS")
                        if vlayer.isValid():
                            QgsProject.instance().addMapLayer(vlayer)
                        else:
                            iface.messageBar().pushWarning("EZiUDP", f"Nie udało się dodać WFS: {layer_name}")

def copy_to_clipboard(text: str):
    clipboard = QApplication.clipboard()
    clipboard.setText(text)



class TerytSelectionDialog(QDialog):
    """Okno wyboru jednostki TERYT z podświetleniem na mapie.
       Pokazuje na liście: TERYT — NAZWA (RODZAJ)
    """
    def __init__(self, iface, layer, teryt_features):
        super().__init__()
        self.iface = iface
        self.layer = layer
        self.teryt_features = teryt_features  # lista QgsFeature
        self.setWindowTitle("Wybór jednostki TERYT")
        self.resize(420, 340)

        layout = QVBoxLayout()
        self.list_widget = QListWidget()
        # Wypełnienie listy: pokaż TERYT — NAZWA (RODZAJ) jeśli pola są dostępne
        for f in teryt_features:
            code = str(f["TERYT"]) if "TERYT" in f.fields().names() else ""
            name = str(f["NAZWA"]) if "NAZWA" in f.fields().names() else (str(f["NAZWA"]) if "Nazwa" in f.fields().names() else "")
            kind = str(f["RODZAJ"]) if "RODZAJ" in f.fields().names() else ""
            display = f"{code} — {name}"
            if kind:
                display += f" ({kind})"
            self.list_widget.addItem(display)
        layout.addWidget(self.list_widget)

        self.btn_ok = QPushButton("Wybierz")
        self.btn_ok.clicked.connect(self.on_accept)
        layout.addWidget(self.btn_ok)
        self.setLayout(layout)

        # RubberBand do podświetlenia (półprzezroczyste wypełnienie + obwódka)
        self.rubber = QgsRubberBand(self.iface.mapCanvas())#, geometryType=QgsRubberBand.Polygon)
        self.rubber.setFillColor(QColor(255, 255, 0, 80))   # półprzezroczyste wypełnienie
        self.rubber.setColor(QColor(255, 200, 0, 200))      # obwódka
        self.rubber.setWidth(2)

        # Reakcja na zmianę wyboru
        self.list_widget.currentRowChanged.connect(self.highlight_feature)

    def highlight_feature(self, index):
        """Podświetla aktualnie zaznaczony obiekt i zoomuje do niego."""
        self.rubber.reset()
        if index < 0 or index >= len(self.teryt_features):
            return
        feature = self.teryt_features[index]
        geom = feature.geometry()
        if geom is None or geom.isEmpty():
            return
        self.rubber.setToGeometry(geom, self.layer)
        try:
            self.iface.mapCanvas().zoomToFeatureIds(self.layer, [feature.id()])
        except Exception:
            # fallback: ustaw widok na bounding box
            bbox = geom.boundingBox()
            self.iface.mapCanvas().setExtent(bbox)
            self.iface.mapCanvas().refresh()

    def get_selected_teryt(self):
        row = self.list_widget.currentRow()
        if row < 0:
            return None
        return str(self.teryt_features[row]["TERYT"])

    def on_accept(self):
        """Zatwierdzenie wyboru + wyczyszczenie zaznaczenia"""
        # resetuj wizualne zaznaczenie natychmiast
        try:
            self.rubber.reset()
        except Exception:
            pass
        self.accept()

    def closeEvent(self, event):
        try:
            self.rubber.reset()
        except Exception:
            pass
        super().closeEvent(event)



# ===============================
# GŁÓWNA KLASA PLUGINU
# ===============================
class Power_EZiUDP:
    def __init__(self, iface):
        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.plugin_dir = os.path.dirname(__file__)
        self.actions = []
        self.menu = self.tr(u'&Power_EZiUDP')
        self.toolbar = self.iface.addToolBar(u'Power_EZiUDP')
        self.toolbar.setObjectName(u'Power_EZiUDP')
        self.pluginIsActive = False
        self.dockwidget = None
        self.debug = QSettings().value("Power_EZiUDP/debug", False, type=bool) # NEW

        self.db_path = os.path.join(self.plugin_dir, "backup.sqlite")
        self.init_db()
        # Wczytaj warstwę GeoJSON z TERYT (jeśli istnieje)
        self.layer_teryt = None
        geojson_path = os.path.join(self.plugin_dir, "powiaty.geojson")
        if os.path.exists(geojson_path):
            self.layer_teryt = QgsVectorLayer(geojson_path, "TERYT_zasiegi", "ogr")
            

    def load_teryt_layer(self):
        rodzaj = self.dockwidget.comboBox.currentText()
        # poprawka wyszukująca tylko warstwy, które powinnny być
        if rodzaj in ["powiaty", "gminy", "wojewodztwa"]: 
            path = os.path.join(self.plugin_dir, f"{rodzaj}.geojson")
            self.layer_teryt = QgsVectorLayer(path, rodzaj, "ogr")

            if not self.layer_teryt.isValid():
                QtWidgets.QMessageBox.warning(None, "Błąd", f"Nie udało się wczytać warstwy {path}")


    def tr(self, message):
        return QCoreApplication.translate('Power_EZiUDP', message)

    def add_action(self, icon_path, text, callback, parent=None):
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        self.toolbar.addAction(action)
        self.iface.addPluginToMenu(self.menu, action)
        self.actions.append(action)
        return action

    def initGui(self):
        icon_path = ':/plugins/Power_EZiUDP/icon.png'
        self.add_action(icon_path, text=self.tr(u'EZiUDP'), callback=self.run, parent=self.iface.mainWindow())

    def onClosePlugin(self):
        """Reakcja na zamknięcie dockwidgetu"""
        try:
            self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)
        except Exception:
            pass
        self.pluginIsActive = False
        if self.dockwidget:
            self.dockwidget.hide()

    
    def unload(self):
        self.pluginIsActive = False
        for action in self.actions:
            self.iface.removePluginMenu(self.menu, action)
            self.iface.removeToolBarIcon(action)
        del self.toolbar

    def log(self, message, level=Qgis.Info):
        """Loguje wiadomość jeśli debug=True; zawsze drukuje do konsoli dla devów."""
        if self.debug:
            QgsMessageLog.logMessage(str(message), "Power_EZiUDP", level)
        else:
            # dla widoczności w konsoli programisty również print
            print(message)

    def build_completer_for_layer(self):
        """Zbuduj i przypisz QCompleter do terytInput na podstawie obecnej layer_teryt."""
        if not hasattr(self, "layer_teryt") or self.layer_teryt is None or not self.layer_teryt.isValid():
            return
        names = []
        # preferowane pole nazwy: 'NAZWA' lub 'JPT_NAZWA_' lub 'nazwa'
        name_field = None
        for candidate in ("NAZWA","Nazwa"):
            if candidate in self.layer_teryt.fields().names():
                name_field = candidate
                break
        if not name_field:
            # fallback: nie buduj completera
            return
        for f in self.layer_teryt.getFeatures():
            val = f[name_field]
            if val:
                names.append(str(val))
        # unikalne i posortowane
        names = sorted(set(names), key=lambda s: s.lower())
        model = QStringListModel(names)
        completer = QCompleter()
        completer.setModel(model)
        completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
        completer.setFilterMode(Qt.MatchFlag.MatchStartsWith)
        self.dockwidget.terytInput.setCompleter(completer)
        # zapamiętaj referencję jeśli potrzeba
        self._teryt_completer = completer



    # ===============================
    # OBSŁUGA KLIKNIĘCIA I TERYT
    # ===============================
    def activate_point_tool(self):
        if not self.layer_teryt:
            QtWidgets.QMessageBox.warning(None, "Brak warstwy", "Brak warstwy GeoJSON z zasięgami TERYT.")
            return
        
        self.iface.messageBar().pushMessage("EZiUDP", "Wskaż punkt na mapie, aby ustalić TERYT", duration=4)
        self.point_tool = PointTool(self.iface, self.point_selected)
        self.iface.mapCanvas().setMapTool(self.point_tool)

    def point_selected(self, point):
        print(f"Kliknięto: {point.x():.2f}, {point.y():.2f}")

        rodzaj = self.dockwidget.comboBox.currentText()
        # teryt = self.get_teryt_from_point(point, rodzaj)
        # self.dockwidget.lineEditTeryt.setText(teryt)

        # self.load_teryt_layer() nie ma potrszeby ładowania przy każdym kliku

        if not self.layer_teryt:
            QtWidgets.QMessageBox.warning(None, "Brak warstwy", "Nie wczytano warstwy TERYT.")
            return

        crs_src = self.iface.mapCanvas().mapSettings().destinationCrs()
        crs_dst = self.layer_teryt.crs()
        transform = QgsCoordinateTransform(crs_src, crs_dst, QgsProject.instance())
        point_layer_crs = transform.transform(point)
        # geom = QgsGeometry.fromPointXY(point_layer_crs)

        # for f in self.layer_teryt.getFeatures(QgsFeatureRequest().setFilterRect(geom.boundingBox())):
        #     if f.geometry().intersects(geom):
        #         teryt = f["TERYT"]
        #         self.dockwidget.terytInput.setText(str(teryt))
        #         self.iface.messageBar().pushMessage("EZiUDP", f"Znaleziono jednostkę: {teryt}", duration=4)
        #         self.search_eziudp()
        #         return

        # Znajdź wszystkie obiekty, które przecinają punkt
        rect = QgsRectangle(point_layer_crs.x() - 0.01, point_layer_crs.y() - 0.01,
                            point_layer_crs.x() + 0.01, point_layer_crs.y() + 0.01)
        
        teryt_list = []
        for feat in self.layer_teryt.getFeatures(QgsFeatureRequest().setFilterRect(rect)):
            geom = feat.geometry()
            if geom.contains(point_layer_crs):
                teryt = feat["TERYT"] if "TERYT" in feat.fields().names() else None
                if teryt:
                    teryt_list.append(teryt)

        # Reakcja w zależności od liczby trafień
        if not teryt_list:
            QtWidgets.QMessageBox.information(None, "EZiUDP", "Nie znaleziono jednostki dla wskazanego punktu.")
            return
        
        if len(teryt_list) == 1:
            selected_teryt = teryt_list[0]
        else:
            # selected_teryt, ok = QtWidgets.QInputDialog.getItem(
            #    None,
            #    "Wybór jednostki",
            #    "Znaleziono kilka jednostek w otoczeniu wybranego punktu.\nWybierz numer TERYT:",
            #    teryt_list,
            #    0,
            #    False
            # )

            # Pobierz pełne featury zamiast samych kodów
            features = []
            for feat in self.layer_teryt.getFeatures(QgsFeatureRequest().setFilterRect(rect)):
                geom = feat.geometry()
                if geom.contains(point_layer_crs):
                    features.append(feat)

            dlg = TerytSelectionDialog(self.iface, self.layer_teryt, features)
            if dlg.exec():
                selected_teryt = dlg.get_selected_teryt()
                if not selected_teryt:
                    return
                    if not ok:
                        return


        # Ustaw numer w interfejsie
        self.dockwidget.terytInput.setText(selected_teryt)
        self.iface.messageBar().pushMessage("EZiUDP", f"Wybrano TERYT: {selected_teryt}", duration=4)
        self.search_eziudp()


        # QtWidgets.QMessageBox.information(None, "Brak", "Nie znaleziono jednostki administracyjnej dla tego punktu.")

    # ===============================
    # WYSZUKIWANIE EZiUDP
    # ===============================
    def search_eziudp(self):
        user_text = self.dockwidget.terytInput.text().strip()
        adres_text = self.dockwidget.adresInput.text().strip()
        fraz_text = self.dockwidget.frazInput.text().strip()
        organ_text = self.dockwidget.jednInput.text().strip()
        jedn = self.dockwidget.comboBox.currentText()

        if not user_text and not adres_text and not fraz_text and not organ_text:
            if jedn != "jednostki centralne":
                QtWidgets.QMessageBox.warning(None, "Błąd", "Podaj numer TERYT lub nazwę jednostki.")
            

            
        if re.search(r"[A-Za-zĄĆĘŁŃÓŚŹŻąćęłńóśźż]", user_text):
            if jedn == "powiaty" and "powiat" not in user_text:
                sel = self.search_by_name_and_show_dialog(f"powiat {user_text}")
            else:
                sel = self.search_by_name_and_show_dialog(user_text)
            if not sel:
                return
            # sel jest teraz wybranym TERYT (kod). Kontynuuj dalej z sel jako teryt.
            teryt = sel
        else:
            # normalne zachowanie: interpretuj jako numer teryt
            teryt = user_text

        
        if jedn == "powiaty":
            if len(teryt) == 3:
                teryt = f"0{teryt}"
            teryt = teryt[:4]
        elif jedn == "wojewodztwa":
            teryt = teryt[:2]
        elif jedn == "gminy":
            teryt = teryt[:6]
        elif jedn == "jednostki centralne":
            jedn = "centralne"
            teryt = "PL"

        # Logika backup
        use_backup = self.dockwidget.checkForceOffline.isChecked()
        html = None

        url = f"https://integracja.gugik.gov.pl/eziudp/index.php?teryt={teryt}&rodzaj={jedn}&nazwa={organ_text}&zbior={fraz_text}&temat=&usluga=&adres={adres_text}"
        
        if not use_backup:
            try:
                html = requests.get(url, timeout=10).text
            except Exception as e:
                QtWidgets.QMessageBox.critical(None, "Błąd", f"Brak połączenia. Szukam w bazie offline...:\n{e}\n")
                use_backup = True
            #return
        
        self.dockwidget.resultsTable.setRowCount(0)

        if not use_backup:

            rows = re.findall(r"<tr.*?>(.*?)</tr>", html, re.S | re.I)
            if not rows:
                QtWidgets.QMessageBox.warning(None, "Brak danych", "Nie znaleziono żadnych zbiorów dla tego TERYT.")
                return

            self.dockwidget.resultsTable.setRowCount(0)

            for row_html in rows[1:]:
                cols = re.findall(r"<td.*?>(.*?)</td>", row_html, re.S | re.I)
                if len(cols) < 2:
                    continue
                nazwa = re.sub(r"<.*?>", "", cols[0]).strip()
                organ = re.sub(r"<.*?>", "", cols[1]).strip()
                rodzaj = re.sub(r"<.*?>", "", cols[2]).strip()
                teryt = re.sub(r"<.*?>", "", cols[3]).strip()
                wms_match = re.search(r'(https?://[^\s"\'<>]+?service=WMS[^\s"\'<>]*)', row_html, re.I)
                wfs_match = re.search(r'(https?://[^\s"\'<>]+?service=WFS[^\s"\'<>]*)', row_html, re.I)
                inspect_match = re.search(r'(index.php[^\s"\'<>]+?edycja[^\s"\'<>]*)', row_html, re.I)
                wms_url = wms_match.group(1) if wms_match else ""
                wfs_url = wfs_match.group(1) if wfs_match else ""
                inspect = inspect_match.group(1) if inspect_match else ""

                #   row = self.dockwidget.resultsTable.rowCount()
                self.add_row_to_table(nazwa, organ, rodzaj, teryt, wms_url, wfs_url, inspect, url)
        else:
            db_results = self.search_in_db(teryt, organ_text, fraz_text, jedn)
            if not db_results:
                QtWidgets.QMessageBox.warning(None, "Brak danych", "Brak wyników w bazie offline (czy wykonałeś aktualizację?).")
                return
            
            for row_data in db_results:
                # row_data zawiera tuple zgodną z kolejnością w SELECT
                self.add_row_to_table(*row_data, url)

            """
            btn_wms = QtWidgets.QPushButton("")
            btn_wfs = QtWidgets.QPushButton("")
            btn_preview = QtWidgets.QPushButton("")
            btn_inspect = QtWidgets.QPushButton("📃")
            btn_teryt = QtWidgets.QPushButton(teryt)
            btn_teryt.setFlat(True)
            btn_teryt.clicked.connect(lambda _, t=teryt: self.zoom_to(t))



            url_count = 0
            if wms_url:
                btn_wms = create_action_button(wms_url, self.iface)
                btn_wms.setIcon(QIcon(":/images/themes/default/mActionAddRasterLayer.svg"))
                # btn_wms.clicked.connect(lambda _, u=wms_url: add_layers_from_service(u, self.iface))
                btn_preview.clicked.connect(lambda _, t=teryt, u=wms_url: self.open_geoportal_preview(t, u))
                btn_preview.setText('🌐')
                url_count += 1
            else:
                btn_wms.setEnabled(False)
                btn_preview.setEnabled(False)
            if wfs_url:
                btn_wfs = create_action_button(wfs_url, self.iface)
                btn_wfs.setIcon(QIcon(":/images/themes/default/mActionAddOgrLayer.svg"))
                # btn_wfs.clicked.connect(lambda _, u=wfs_url: add_layers_from_service(u, self.iface))
                url_count += 1
            else:
                btn_wfs.setEnabled(False)
            if inspect:
                inspect_url = f"https://integracja.gugik.gov.pl/eziudp/{inspect}"
                btn_inspect.clicked.connect(lambda _, i=inspect_url: QDesktopServices.openUrl(QUrl(i)))
            else:
                btn_inspect.setEnabled(False) 

            if url_count == 0 and self.dockwidget.checkSkipEmptyUrl.isChecked():
                continue

            # dodawanie danych na końcu powinno mi pozwolić na omijanie zbiorów bez url
            self.dockwidget.resultsTable.insertRow(row)
            self.dockwidget.resultsTable.setItem(row, 0, QtWidgets.QTableWidgetItem(nazwa))
            self.dockwidget.resultsTable.setItem(row, 1, QtWidgets.QTableWidgetItem(organ))
            self.dockwidget.resultsTable.setItem(row, 2, QtWidgets.QTableWidgetItem(rodzaj))
            # self.dockwidget.resultsTable.setItem(row, 3, QtWidgets.QTableWidgetItem(teryt))
            self.dockwidget.resultsTable.setCellWidget(row, 3, btn_teryt)
            self.dockwidget.resultsTable.setCellWidget(row, 4, btn_wms)
            self.dockwidget.resultsTable.setCellWidget(row, 5, btn_wfs)
            self.dockwidget.resultsTable.setCellWidget(row, 6, btn_preview)
            self.dockwidget.resultsTable.setCellWidget(row, 7, btn_inspect)
            """

    def search_by_name_and_show_dialog(self, name_query):
        """Szukaj w layer_teryt jednostek zaczynających się od name_query (case-insensitive).
        Otwórz TerytSelectionDialog z wynikami (TERYT, NAZWA, RODZAJ).
        """
        if not self.layer_teryt or not self.layer_teryt.isValid():
            QtWidgets.QMessageBox.warning(None, "Brak warstwy", "Nie wczytano warstwy jednostek.")
            return None

        # wybierz pole nazwy (tak jak w completer)
        name_field = None
        for candidate in ("NAZWA","JPT_NAZWA_","Nazwa","NAZWA_"):
            if candidate in self.layer_teryt.fields().names():
                name_field = candidate
                break
        if not name_field:
            QtWidgets.QMessageBox.information(None, "Brak pola nazwy", "Warstwa nie zawiera pola nazwy jednostki.")
            return None

        query = name_query.strip().lower()
        matches = []
        # filtr przestrzenny niepotrzebny — przeszukujemy wszystkie cechy
        for feat in self.layer_teryt.getFeatures():
            val = feat[name_field]
            if val and str(val).lower().startswith(query):
                matches.append(feat)

        if not matches:
            QtWidgets.QMessageBox.information(None, "Brak wyników", f"Nie znaleziono jednostek zaczynających się od '{name_query}'.")
            return None

        # otwórz dialog do wyboru (z podświetleniem)
        dlg = TerytSelectionDialog(self.iface, self.layer_teryt, matches)
        if dlg.exec():
            sel = dlg.get_selected_teryt()
            if sel:
                # ustaw w polu i zapisz do historii
                self.dockwidget.terytInput.setText(sel)
                # self.push_history(sel)
                # możesz od razu wykonać search_eziudp() dla sel — lub zwrócić sel
                return sel
        return None


    def open_geoportal_preview(self, teryt, wms_url):
        """Otwiera podgląd WMS w Geoportalu z bbox jednostki."""
        if not self.layer_teryt or not teryt:
            QtWidgets.QMessageBox.warning(None, "Brak danych", "Brak warstwy TERYT lub numeru.")
            return

        # Skróć TERYT w zależności od typu jednostki
        if len(teryt) >= 6:
            filter_prefix = teryt[:6]
        elif len(teryt) == 4:
            filter_prefix = teryt[:4]
        elif len(teryt) == 3:
            filter_prefix = f"0{teryt}"
        else:
            filter_prefix = teryt[:2]


        bbox = None
        for f in self.layer_teryt.getFeatures():
            val = str(f["TERYT"])
            if val.startswith(filter_prefix):
                geom = f.geometry()
                rect = geom.boundingBox()
                bbox = (rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum())
                break

        
        if not bbox:
            # QtWidgets.QMessageBox.information(None, "Brak danych", f"Nie znaleziono zasięgu dla TERYT {teryt}")
            url = f"https://mapy.geoportal.gov.pl/imap/Imgp_2.html?SRS=2180&resources=map:wms@{wms_url}"
            QDesktopServices.openUrl(QUrl(url))
            return

        bbox_str = ",".join([f"{c:.2f}" for c in bbox])
        url = f"https://mapy.geoportal.gov.pl/imap/Imgp_2.html?SRS=2180&resources=map:wms@{wms_url}&bbox={bbox_str}"

        QDesktopServices.openUrl(QUrl(url))

    def zoom_to(self, teryt):

        if not self.layer_teryt or not teryt:
            QtWidgets.QMessageBox.warning(None, "Brak danych", "Brak warstwy TERYT lub numeru.")
            return

        # Skróć TERYT w zależności od typu jednostki
        if len(teryt) >= 6:
            filter_prefix = teryt[:6]
        elif len(teryt) == 4:
            filter_prefix = teryt[:4]
        elif len(teryt) == 3:
            filter_prefix = f"0{teryt}"
        else:
            filter_prefix = teryt[:2]


        bbox = None
        for f in self.layer_teryt.getFeatures():
            val = str(f["TERYT"])
            if val.startswith(filter_prefix):
                geom = f.geometry()
                rect = geom.boundingBox()
                bbox = (rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum())
                break

        try:

            self.canvas.setExtent(rect)
            self.canvas.refresh()

            self.iface.messageBar().pushMessage("Przybliżono do: ",
                                                 f"{filter_prefix}",
                                                   level=Qgis.Success, duration=5)
        except Exception as e:
            self.iface.messageBar().pushMessage("Error: ",
                                                 f"{e}",
                                                   level=Qgis.Critical, duration=5)

    def validate_with_coordinates(self, url, dane):
        """Uruchamia narzędzie wskazywania punktu, a następnie waliduje usługę WMS w tym punkcie."""
        
        def on_point(point):
            # Transformacja do EPSG:2180
            canvas_crs = self.iface.mapCanvas().mapSettings().destinationCrs()
            target_crs = QgsCoordinateReferenceSystem("EPSG:2180")
            xform = QgsCoordinateTransform(canvas_crs, target_crs, QgsProject.instance())
            pt_2180 = xform.transform(point)
            
            clean_url = url.split("?")[0]
            # https://walidator.gugik.gov.pl/service?url=...&dane=...&typ=WMS&CRS=EPSG:2180&X=...&Y=...
            # Zapewnienie, że dane są uppercase (np. GESUT, BDSOG)
            dane_str = dane.upper()
            
            val_url = (f"https://walidator.gugik.gov.pl/service?"
                       f"url={clean_url}&dane={dane_str}&typ=WMS&CRS=EPSG:2180"
                       f"&X={pt_2180.y():.2f}&Y={pt_2180.x():.2f}")
            
            QgsMessageLog.logMessage(f"Walidacja usługi (współrzędne): {val_url}", "Power_EZiUDP", Qgis.Info)
            webbrowser.open_new(val_url)
            self.iface.messageBar().pushMessage("EZiUDP", f"Otwarto walidator dla {dane_str} w przeglądarce.", duration=4)

        self.iface.messageBar().pushMessage("EZiUDP", f"Wskaż obiekt ({dane}) na mapie do walidacji...", duration=5)
        self.point_tool = PointTool(self.iface, on_point)
        self.iface.mapCanvas().setMapTool(self.point_tool)

    def create_action_button(self, url, teryt="X", baza="X"):
        btn = QToolButton()
        btn.setText("Dodaj")
        btn.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)

        # Menu rozwijane
        menu = QMenu()

        # Akcje standardowe
        add_action = QAction("Dodaj warstwy", btn)
        copy_action = QAction("Kopiuj adres URL", btn)
        copy_gc = QAction("Kopiuj adres Get Capabilities", btn)
        open_gc = QAction("Otwórz adres Get Capabilities w przeglądarce", btn)

        # Podpięcie akcji
        btn.clicked.connect(lambda: add_layers_from_service(url, self.iface))
        add_action.triggered.connect(lambda: add_layers_from_service(url, self.iface))
        copy_gc.triggered.connect(lambda: copy_to_clipboard(url))
        open_gc.triggered.connect(lambda _, i=url: QDesktopServices.openUrl(QUrl(i)))
        copy_action.triggered.connect(lambda: copy_to_clipboard(url.split("?")[0] if "?" in url else url))

        # Dodanie do menu
        menu.addAction(add_action)
        menu.addSeparator()
        menu.addAction(copy_gc)
        menu.addAction(open_gc)
        menu.addAction(copy_action)
        menu.addSeparator()

        if teryt != "X" and baza != "X":
            # 1. Walidacja ogólna (EGiB i inne - przekierowanie do walidatora bez współrzędnych lub z terytem)
            add_walidator = QAction("Waliduj usługę", btn)
            add_walidator.triggered.connect(lambda: validate_service(url, teryt, baza, self.iface))
            menu.addAction(add_walidator)

            # 2. Walidacja ze współrzędnymi (RCN, BDOT500, GESUT, BDSOG)
            # Próba wyciągnięcia kodu bazy z nawiasu np. "Geodezyjna Ewidencja ... (GESUT)" -> GESUT
            try:
                wzorzec = r'\((.*?)\)'
                matches = re.findall(wzorzec, baza)
                if matches:
                    dane_code = matches[0].upper()
                    supported_coords = ["RCN", "BDOT500", "GESUT", "BDSOG"]
                    
                    if dane_code in supported_coords and "service=wfs" not in url.lower():
                        add_walidator_coords = QAction("Waliduj usługę (wskazanie obiektu)", btn)
                        add_walidator_coords.triggered.connect(lambda: self.validate_with_coordinates(url, dane_code))
                        menu.addAction(add_walidator_coords)
            except Exception as e:
                print(f"Błąd parsowania bazy dla walidatora: {e}")

        btn.setMenu(menu)
        return btn
    
    def create_detail_button(self, inspect_url, organ, url):
        btn = QToolButton()
        btn.setText("📃")
        btn.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)

        # Menu rozwijane
        menu = QMenu()

        # Akcje standardowe
        

        # Podpięcie akcji
        btn.clicked.connect(lambda _, i=inspect_url: QDesktopServices.openUrl(QUrl(i)))

        inspect_action = QAction("Pokaż szczegóły", btn)
        inspect_action.triggered.connect(lambda _, i=inspect_url: QDesktopServices.openUrl(QUrl(i)))
        menu.addAction(inspect_action)
        
        menu.addSeparator()

        query_action = QAction("Googluj organ", btn)
        query_action.triggered.connect(lambda _, i=f"https://www.google.com/search?q={organ}": QDesktopServices.openUrl(QUrl(i)))
        menu.addAction(query_action)

        organ_action = QAction("Pokaż zbiory organu na stronie EZiUDP", btn)
        organ_action.triggered.connect(lambda _, i=f"https://integracja.gugik.gov.pl/eziudp/index.php?&nazwa={organ}": QDesktopServices.openUrl(QUrl(i)))
        menu.addAction(organ_action)

        url_action = QAction("Pokaż wyszukiwanie na stronie EZiUDP", btn)
        url_action.triggered.connect(lambda _, i=url: QDesktopServices.openUrl(QUrl(i)))
        menu.addAction(url_action)

        btn.setMenu(menu)
        return btn

    
    def on_unit_type_changed(self, idx):
        self.load_teryt_layer()
        self.build_completer_for_layer()

    def add_row_to_table(self, nazwa, organ, rodzaj, teryt, wms_url, wfs_url, inspect, url):
        """Pomocnicza metoda dodająca wiersz do tabeli wyników."""
        row = self.dockwidget.resultsTable.rowCount()
        
        btn_wms = QtWidgets.QPushButton("")
        btn_wfs = QtWidgets.QPushButton("")
        btn_preview = QtWidgets.QPushButton("")
        btn_inspect = QtWidgets.QPushButton("📃")
        btn_teryt = QtWidgets.QPushButton(teryt)
        btn_teryt.setFlat(True)
        btn_teryt.clicked.connect(lambda _, t=teryt: self.zoom_to(t))

        url_count = 0
        if wms_url:
            if len(teryt) == 4:
                btn_wms = self.create_action_button(wms_url, teryt, rodzaj)
            else:       
                btn_wms = self.create_action_button(wms_url)
            btn_wms.setIcon(QIcon(":/images/themes/default/mActionAddRasterLayer.svg"))
            btn_preview.clicked.connect(lambda _, t=teryt, u=wms_url: self.open_geoportal_preview(t, u))
            btn_preview.setText('🌐')
            url_count += 1
        else:
            btn_wms.setEnabled(False)
            btn_preview.setEnabled(False)
            
        if wfs_url:
            if len(teryt) == 4:
                btn_wfs = self.create_action_button(wfs_url, teryt, rodzaj)
            else:
                btn_wfs = self.create_action_button(wfs_url)
            btn_wfs.setIcon(QIcon(":/images/themes/default/mActionAddOgrLayer.svg"))
            url_count += 1
        else:
            btn_wfs.setEnabled(False)
            
        if inspect:
            if not inspect.startswith("http"):
                inspect_url = f"https://integracja.gugik.gov.pl/eziudp/{inspect}"

            else:
                inspect_url = inspect

            btn_inspect = self.create_detail_button(inspect_url, organ, url)
            # btn_inspect.clicked.connect(lambda _, i=inspect_url: QDesktopServices.openUrl(QUrl(i)))
        else:
            btn_inspect.setEnabled(False) 

        if url_count == 0 and self.dockwidget.checkSkipEmptyUrl.isChecked():
            return

        self.dockwidget.resultsTable.insertRow(row)
        self.dockwidget.resultsTable.setItem(row, 0, QtWidgets.QTableWidgetItem(nazwa))
        self.dockwidget.resultsTable.setItem(row, 1, QtWidgets.QTableWidgetItem(organ))
        self.dockwidget.resultsTable.setItem(row, 2, QtWidgets.QTableWidgetItem(rodzaj))
        self.dockwidget.resultsTable.setCellWidget(row, 3, btn_teryt)
        self.dockwidget.resultsTable.setCellWidget(row, 4, btn_wms)
        self.dockwidget.resultsTable.setCellWidget(row, 5, btn_wfs)
        self.dockwidget.resultsTable.setCellWidget(row, 6, btn_preview)
        self.dockwidget.resultsTable.setCellWidget(row, 7, btn_inspect)

    # ===============================
    # URUCHOMIENIE PLUGINU
    # ===============================
    def run(self):
        if not self.pluginIsActive:
            self.pluginIsActive = True
            if self.dockwidget is None:
                self.dockwidget = Power_EZiUDPDockwidget()
                self.dockwidget.searchButton.clicked.connect(self.search_eziudp)
                self.dockwidget.pointButton.clicked.connect(self.activate_point_tool)
                self.iface.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.dockwidget)
                self.dockwidget.comboBox.currentIndexChanged.connect(self.on_unit_type_changed)
                self.dockwidget.updateButton.clicked.connect(self.update_database)
                self.build_completer_for_layer()

            self.dockwidget.show()
        else:
            self.dockwidget.show()
            self.dockwidget.raise_()


    # ===============================
    # OBSŁUGA BAZY DANYCH SQLITE
    # ===============================
    def init_db(self):
        """Tworzy tabelę w SQLite jeśli nie istnieje."""
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            # Tworzymy tabelę z constraintem UNIQUE, aby unikać duplikatów
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS eziudp_data (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    nazwa TEXT,
                    organ TEXT,
                    rodzaj TEXT,
                    teryt TEXT,
                    wms_url TEXT,
                    wfs_url TEXT,
                    inspect_url TEXT,
                    UNIQUE(nazwa, teryt, wms_url, wfs_url)
                )
            """)
            conn.commit()
            conn.close()
        except Exception as e:
            self.log(f"Błąd inicjalizacji DB: {e}", Qgis.Critical)

    def update_database(self):
        """Pobiera wszystkie dane z ?showall i zapisuje do bazy."""
        url = "https://integracja.gugik.gov.pl/eziudp/index.php?showall"
        
        # Zmieniamy kursor na "oczekiwanie", bo pobieranie dużej strony może trwać
        QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
        try:
            self.iface.messageBar().pushMessage("EZiUDP", "Pobieranie danych do backupu...", level=Qgis.Info)
            response = requests.get(url, timeout=30) # Dłuższy timeout dla dużej listy
            html = response.text
        except Exception as e:
            QApplication.restoreOverrideCursor()
            QtWidgets.QMessageBox.critical(None, "Błąd", f"Nie udało się pobrać danych do backupu:\n{e}")
            return

        rows = re.findall(r"<tr.*?>(.*?)</tr>", html, re.S | re.I)
        if not rows:
            QApplication.restoreOverrideCursor()
            return

        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            # Opcjonalnie: Wyczyść stare dane, aby baza nie rosła w nieskończoność
            cursor.execute("DELETE FROM eziudp_data")
            
            count = 0
            for row_html in rows[1:]: # pomijamy nagłówek tabeli
                cols = re.findall(r"<td.*?>(.*?)</td>", row_html, re.S | re.I)
                if len(cols) < 2:
                    continue
                
                # Parsowanie HTML (to samo co przy zwykłym szukaniu)
                nazwa = re.sub(r"<.*?>", "", cols[0]).strip()
                organ = re.sub(r"<.*?>", "", cols[1]).strip()
                rodzaj = re.sub(r"<.*?>", "", cols[2]).strip()
                teryt = re.sub(r"<.*?>", "", cols[3]).strip()
                
                wms_match = re.search(r'(https?://[^\s"\'<>]+?service=WMS[^\s"\'<>]*)', row_html, re.I)
                wfs_match = re.search(r'(https?://[^\s"\'<>]+?service=WFS[^\s"\'<>]*)', row_html, re.I)
                inspect_match = re.search(r'(index.php[^\s"\'<>]+?edycja[^\s"\'<>]*)', row_html, re.I)
                
                wms_url = wms_match.group(1) if wms_match else ""
                wfs_url = wfs_match.group(1) if wfs_match else ""
                inspect = inspect_match.group(1) if inspect_match else ""

                cursor.execute("""
                    INSERT OR IGNORE INTO eziudp_data (nazwa, organ, rodzaj, teryt, wms_url, wfs_url, inspect_url)
                    VALUES (?, ?, ?, ?, ?, ?, ?)
                """, (nazwa, organ, rodzaj, teryt, wms_url, wfs_url, inspect))
                count += 1
            
            conn.commit()
            conn.close()
            self.iface.messageBar().pushMessage("EZiUDP", f"Zaktualizowano bazę offline. Rekordów: {count}", level=Qgis.Success)
        
        except Exception as e:
            QtWidgets.QMessageBox.critical(None, "Błąd Bazy", f"Błąd zapisu do SQLite:\n{e}")
        finally:
            QApplication.restoreOverrideCursor()

    def search_in_db(self, teryt, organ_pattern, fraz_pattern, jedn):
        """Wyszukuje w lokalnej bazie SQLite."""
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            query = "SELECT nazwa, organ, rodzaj, teryt, wms_url, wfs_url, inspect_url FROM eziudp_data WHERE 1=1"
            params = []

            if jedn == "powiaty":
                if len(teryt) == 3:
                    teryt = f"0{teryt}"
                teryt = teryt[:4]
            elif jedn == "wojewodztwa":
                teryt = teryt[:2]
            elif jedn == "gminy":
                teryt = teryt[:6]
            elif jedn == "jednostki centralne":
                jedn = "centralne"
                teryt = "PL"
            
            # Budowanie zapytania SQL dynamicznie
            if teryt:
                if jedn == "powiaty":
                   query += " AND teryt LIKE ? AND LENGTH(teryt) == 4" 
                   params.append(f"{teryt}%")
                elif jedn == "gminy":
                    query += " AND teryt LIKE ? AND LENGTH(teryt) > 4"
                    params.append(f"{teryt}%")
                elif jedn == "wojewodztwa":
                    query += " AND teryt LIKE ? AND LENGTH(teryt) == 2"
                    params.append(f"{teryt}%")
                else:
                    query += " AND teryt LIKE ?"
                    params.append(f"{teryt}%")
            
            if organ_pattern:
                query += " AND organ LIKE ?"
                params.append(f"%{organ_pattern}%")
                
            if fraz_pattern:
                query += " AND nazwa LIKE ?"
                params.append(f"%{fraz_pattern}%")
                
            cursor.execute(query, params)
            results = cursor.fetchall()
            conn.close()
            return results
        except Exception as e:
            self.log(f"Błąd odczytu DB: {e}", Qgis.Critical)
            return []

