"""Tab 3: Merge multiple SHP files into a single KML / KMZ.

Provides full label / popup / style / highlight configuration per layer.
Each geometry tab has a dropdown to select which layer to configure.
"""

import os
from qgis.PyQt.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
    QLabel, QComboBox, QLineEdit, QPushButton, QSpinBox,
    QFileDialog, QMessageBox, QTableWidget, QTableWidgetItem,
    QHeaderView, QCheckBox, QColorDialog, QGroupBox,
    QSlider, QScrollArea, QFrame, QDoubleSpinBox, QSplitter,
    QTextEdit
)
from qgis.PyQt.QtCore import Qt, QPoint, QRect
from qgis.PyQt.QtGui import (
    QColor, QPainter, QPen, QBrush, QPolygon, QPainterPath
)
from qgis.core import QgsProject, QgsVectorLayer, QgsWkbTypes
from .merge_builder import MergeKmlBuilder
from .html_template import HtmlTemplateBuilder


# ── Geometry-type config panel ────────────────────────

class _GeomConfigPanel(QWidget):
    """Config panel for one geometry type with per-layer dropdown.

    Features:
      - Per-layer config save / restore via dropdown
      - Popup toggle (disable popup while keeping style)
      - Highlight column (★) with condition for conditional formatting
    """

    def __init__(self, geom_key, on_change=None, parent=None):
        super().__init__(parent)
        self.geom_key = geom_key
        self._fields = []
        self._sample_data = {}
        self._on_change = on_change
        self._loading = False
        # Per-layer storage
        self._layer_keys = []
        self._layer_names = {}
        self._layer_fields = {}
        self._layer_samples = {}
        self._layer_states = {}
        self._current_key = None
        self._setup_ui()
        self._connect_signals()

    # ── UI construction ───────────────────────────────

    def _setup_ui(self):
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setFrameShape(QFrame.NoFrame)
        inner = QWidget()
        ly = QVBoxLayout(inner)
        scroll.setWidget(inner)
        outer = QVBoxLayout(self)
        outer.setContentsMargins(0, 0, 0, 0)
        outer.addWidget(scroll)

        # 0 — Layer selector
        gp_layer = QGroupBox("Layer")
        layer_ly = QHBoxLayout()
        layer_ly.addWidget(QLabel("Chọn layer:"))
        self.cbo_layer = QComboBox()
        self.cbo_layer.setMinimumWidth(200)
        self.cbo_layer.setStyleSheet(
            "QComboBox{font-weight:bold;padding:3px}")
        layer_ly.addWidget(self.cbo_layer, 1)
        gp_layer.setLayout(layer_ly)
        ly.addWidget(gp_layer)

        # 1 — Name settings
        gp_name = QGroupBox("Nhãn (Label)")
        name_main = QVBoxLayout()
        n_ly = QHBoxLayout()
        self.chk_f1 = QCheckBox("F1:")
        self.chk_f1.setChecked(True)
        self.cbo_f1 = QComboBox()
        self.txt_sep = QLineEdit(" - ")
        self.txt_sep.setFixedWidth(50)
        self.chk_f2 = QCheckBox("F2:")
        self.cbo_f2 = QComboBox()
        self.spn_size = QSpinBox()
        self.spn_size.setRange(8, 72)
        self.spn_size.setValue(12)
        self.btn_name_clr = QPushButton("#000000")
        self.btn_name_clr.setFixedWidth(70)
        self.btn_name_clr.setStyleSheet(
            "background:#000000;color:white")
        n_ly.addWidget(self.chk_f1)
        n_ly.addWidget(self.cbo_f1, 1)
        n_ly.addWidget(QLabel("Sep:"))
        n_ly.addWidget(self.txt_sep)
        n_ly.addWidget(self.chk_f2)
        n_ly.addWidget(self.cbo_f2, 1)
        n_ly.addWidget(QLabel("Size:"))
        n_ly.addWidget(self.spn_size)
        n_ly.addWidget(self.btn_name_clr)
        name_main.addLayout(n_ly)
        self.lbl_name_preview = QLabel(
            "<b>Preview:</b> <i>—</i>")
        self.lbl_name_preview.setStyleSheet("color: #1B5E20;")
        name_main.addWidget(self.lbl_name_preview)
        gp_name.setLayout(name_main)
        ly.addWidget(gp_name)

        # 2 — Popup fields
        gp_popup = QGroupBox("Popup (Fields)")
        p_ly = QVBoxLayout()

        # Popup toggle
        self.chk_popup = QCheckBox("Bật Popup thông tin")
        self.chk_popup.setChecked(True)
        self.chk_popup.setStyleSheet(
            "font-weight:bold;color:#1B5E20")
        p_ly.addWidget(self.chk_popup)

        btn_row = QHBoxLayout()
        self.btn_chk_all = QPushButton("✅ All")
        self.btn_chk_all.setFixedWidth(70)
        self.btn_chk_all.clicked.connect(self._check_all)
        self.btn_unchk = QPushButton("❎ None")
        self.btn_unchk.setFixedWidth(70)
        self.btn_unchk.clicked.connect(self._uncheck_all)
        btn_row.addWidget(self.btn_chk_all)
        btn_row.addWidget(self.btn_unchk)
        btn_row.addStretch()
        self.btn_up = QPushButton("↑")
        self.btn_up.setFixedWidth(30)
        self.btn_up.clicked.connect(self._move_up)
        self.btn_down = QPushButton("↓")
        self.btn_down.setFixedWidth(30)
        self.btn_down.clicked.connect(self._move_down)
        btn_row.addWidget(self.btn_up)
        btn_row.addWidget(self.btn_down)
        p_ly.addLayout(btn_row)

        # Table: 5 columns — √, Field, Alias, Unit, ★
        self.tbl_fields = QTableWidget(0, 5)
        self.tbl_fields.setHorizontalHeaderLabels(
            ["√", "Field", "Alias", "Unit", "★"])
        hdr = self.tbl_fields.horizontalHeader()
        hdr.setSectionResizeMode(0, QHeaderView.Fixed)
        hdr.setSectionResizeMode(1, QHeaderView.Stretch)
        hdr.setSectionResizeMode(2, QHeaderView.Stretch)
        hdr.setSectionResizeMode(3, QHeaderView.Stretch)
        hdr.setSectionResizeMode(4, QHeaderView.Fixed)
        self.tbl_fields.setColumnWidth(0, 30)
        self.tbl_fields.setColumnWidth(4, 30)
        self.tbl_fields.setMinimumHeight(120)
        p_ly.addWidget(self.tbl_fields)

        # Highlight condition row
        hl_row = QHBoxLayout()
        self.chk_highlight = QCheckBox("Highlight:")
        self.chk_highlight.setStyleSheet(
            "font-weight:bold;color:#C62828")
        hl_row.addWidget(self.chk_highlight)
        hl_row.addWidget(QLabel("Khi"))
        self.cbo_hl_op = QComboBox()
        self.cbo_hl_op.addItems([">", ">=", "<", "<=", "=", "!="])
        self.cbo_hl_op.setFixedWidth(55)
        hl_row.addWidget(self.cbo_hl_op)
        self.txt_hl_val = QLineEdit("1")
        self.txt_hl_val.setFixedWidth(80)
        self.txt_hl_val.setPlaceholderText("Giá trị")
        hl_row.addWidget(self.txt_hl_val)
        hl_row.addWidget(QLabel("→"))
        self.lbl_hl_style = QLabel(
            " <b style='color:#0000CC;"
            "background:#FFE0E0;padding:2px 6px'>"
            "Bôi đậm</b> ")
        hl_row.addWidget(self.lbl_hl_style)
        hl_row.addStretch()
        p_ly.addLayout(hl_row)

        gp_popup.setLayout(p_ly)
        ly.addWidget(gp_popup)

        # 3 — Style (geometry-dependent)
        gp_style = QGroupBox("Style")
        s_ly = QHBoxLayout()

        if self.geom_key == 'point':
            s_ly.addWidget(QLabel("Icon:"))
            self.btn_icon_clr = QPushButton("#FF4444")
            self.btn_icon_clr.setStyleSheet("background:#FF4444")
            s_ly.addWidget(self.btn_icon_clr)
            s_ly.addWidget(QLabel("Scale:"))
            self.spn_icon_scale = QDoubleSpinBox()
            self.spn_icon_scale.setRange(0.3, 5.0)
            self.spn_icon_scale.setSingleStep(0.1)
            self.spn_icon_scale.setValue(1.0)
            s_ly.addWidget(self.spn_icon_scale)

        elif self.geom_key == 'line':
            s_ly.addWidget(QLabel("Color:"))
            self.btn_line_clr = QPushButton("#0000FF")
            self.btn_line_clr.setStyleSheet("background:#0000FF")
            s_ly.addWidget(self.btn_line_clr)
            s_ly.addWidget(QLabel("Width:"))
            self.spn_line_w = QSpinBox()
            self.spn_line_w.setRange(1, 20)
            self.spn_line_w.setValue(2)
            s_ly.addWidget(self.spn_line_w)

        else:  # polygon
            s_ly.addWidget(QLabel("Border:"))
            self.btn_border = QPushButton("#FF0000")
            self.btn_border.setStyleSheet("background:#FF0000")
            s_ly.addWidget(self.btn_border)
            s_ly.addWidget(QLabel("W:"))
            self.spn_bw = QSpinBox()
            self.spn_bw.setRange(1, 20)
            self.spn_bw.setValue(2)
            s_ly.addWidget(self.spn_bw)
            s_ly.addWidget(QLabel("Fill:"))
            self.btn_fill = QPushButton("#00FF00")
            self.btn_fill.setStyleSheet("background:#00FF00")
            s_ly.addWidget(self.btn_fill)
            s_ly.addWidget(QLabel("Op:"))
            self.sld_op = QSlider(Qt.Horizontal)
            self.sld_op.setRange(0, 100)
            self.sld_op.setValue(50)
            s_ly.addWidget(self.sld_op)

        gp_style.setLayout(s_ly)
        ly.addWidget(gp_style)

        # 4 — Header
        gp_hdr = QGroupBox("Header Popup")
        h_ly = QHBoxLayout()
        h_ly.addWidget(QLabel("Title:"))
        self.txt_header = QLineEdit("Thông tin")
        h_ly.addWidget(self.txt_header, 1)
        self.btn_hdr_bg = QPushButton("#1B5E20")
        self.btn_hdr_bg.setStyleSheet("background:#1B5E20")
        h_ly.addWidget(self.btn_hdr_bg)
        gp_hdr.setLayout(h_ly)
        ly.addWidget(gp_hdr)

        ly.addStretch()

    # ── Signal connections ────────────────────────────

    def _connect_signals(self):
        self.cbo_layer.currentIndexChanged.connect(
            self._on_layer_changed)

        widgets = [
            self.cbo_f1, self.cbo_f2, self.txt_sep,
            self.spn_size, self.chk_f1, self.chk_f2,
            self.txt_header, self.chk_popup,
            self.chk_highlight, self.cbo_hl_op,
            self.txt_hl_val,
        ]
        if self.geom_key == 'point':
            widgets.extend([self.spn_icon_scale])
        elif self.geom_key == 'line':
            widgets.extend([self.spn_line_w])
        else:
            widgets.extend([self.spn_bw, self.sld_op])

        for w in widgets:
            if hasattr(w, 'textChanged'):
                w.textChanged.connect(self._notify)
            if hasattr(w, 'valueChanged'):
                w.valueChanged.connect(self._notify)
            if hasattr(w, 'toggled'):
                w.toggled.connect(self._notify)
            if hasattr(w, 'currentIndexChanged'):
                w.currentIndexChanged.connect(self._notify)

        color_btns = [self.btn_name_clr, self.btn_hdr_bg]
        if self.geom_key == 'point':
            color_btns.append(self.btn_icon_clr)
        elif self.geom_key == 'line':
            color_btns.append(self.btn_line_clr)
        else:
            color_btns.extend([self.btn_border, self.btn_fill])
        for btn in color_btns:
            btn.clicked.connect(
                lambda checked, b=btn: (
                    self._pick(b), self._notify()))

        self.tbl_fields.itemChanged.connect(self._notify)
        self.chk_f1.toggled.connect(self.cbo_f1.setEnabled)
        self.chk_f2.toggled.connect(self.cbo_f2.setEnabled)

    # ── Layer switching ───────────────────────────────

    def _on_layer_changed(self, idx):
        if idx < 0 or self._loading:
            return
        if idx >= len(self._layer_keys):
            return
        self._save_current()
        self._current_key = self._layer_keys[idx]
        self._load_layer(self._current_key)

    def _save_current(self):
        if self._current_key and not self._loading:
            self._layer_states[self._current_key] = (
                self._save_state())

    def _save_state(self):
        state = {
            'f1_checked': self.chk_f1.isChecked(),
            'f1_field': self.cbo_f1.currentText(),
            'f2_checked': self.chk_f2.isChecked(),
            'f2_field': self.cbo_f2.currentText(),
            'separator': self.txt_sep.text(),
            'font_size': self.spn_size.value(),
            'name_color': self.btn_name_clr.text(),
            'header_title': self.txt_header.text(),
            'header_bg': self.btn_hdr_bg.text(),
            'popup_enabled': self.chk_popup.isChecked(),
            'highlight_enabled': self.chk_highlight.isChecked(),
            'highlight_op': self.cbo_hl_op.currentText(),
            'highlight_val': self.txt_hl_val.text(),
            'fields': [],
        }
        for i in range(self.tbl_fields.rowCount()):
            chk = self.tbl_fields.cellWidget(i, 0)
            fi = self.tbl_fields.item(i, 1)
            ai = self.tbl_fields.item(i, 2)
            si = self.tbl_fields.item(i, 3)
            hl = self.tbl_fields.cellWidget(i, 4)
            state['fields'].append({
                'checked': chk.isChecked() if chk else True,
                'field': fi.text() if fi else '',
                'alias': ai.text() if ai else '',
                'unit': si.text() if si else '',
                'highlight': hl.isChecked() if hl else False,
            })
        if self.geom_key == 'point':
            state['icon_color'] = self.btn_icon_clr.text()
            state['icon_scale'] = self.spn_icon_scale.value()
        elif self.geom_key == 'line':
            state['line_color'] = self.btn_line_clr.text()
            state['line_width'] = self.spn_line_w.value()
        else:
            state['border_color'] = self.btn_border.text()
            state['border_width'] = self.spn_bw.value()
            state['fill_color'] = self.btn_fill.text()
            state['fill_opacity'] = self.sld_op.value()
        return state

    def _load_layer(self, key):
        self._loading = True
        fields = self._layer_fields.get(key, [])
        sample = self._layer_samples.get(key, {})
        state = self._layer_states.get(key)
        self._sample_data = sample
        self._fields = list(fields)

        # Block signals
        for w in [self.tbl_fields, self.cbo_f1, self.cbo_f2,
                  self.chk_f1, self.chk_f2, self.txt_sep,
                  self.spn_size, self.txt_header,
                  self.chk_popup, self.chk_highlight,
                  self.cbo_hl_op, self.txt_hl_val]:
            w.blockSignals(True)

        self.cbo_f1.clear()
        self.cbo_f1.addItems(fields)
        self.cbo_f2.clear()
        self.cbo_f2.addItems(fields)

        if state:
            self.chk_f1.setChecked(
                state.get('f1_checked', True))
            f1 = state.get('f1_field', '')
            idx1 = self.cbo_f1.findText(f1)
            if idx1 >= 0:
                self.cbo_f1.setCurrentIndex(idx1)
            self.chk_f2.setChecked(
                state.get('f2_checked', False))
            f2 = state.get('f2_field', '')
            idx2 = self.cbo_f2.findText(f2)
            if idx2 >= 0:
                self.cbo_f2.setCurrentIndex(idx2)
            self.txt_sep.setText(
                state.get('separator', ' - '))
            self.spn_size.setValue(
                state.get('font_size', 12))
            nc = state.get('name_color', '#000000')
            self.btn_name_clr.setText(nc)
            self.btn_name_clr.setStyleSheet(
                f"background-color:{nc}")
            self.txt_header.setText(
                state.get('header_title', 'Thông tin'))
            hbg = state.get('header_bg', '#1B5E20')
            self.btn_hdr_bg.setText(hbg)
            self.btn_hdr_bg.setStyleSheet(
                f"background-color:{hbg}")
            self.chk_popup.setChecked(
                state.get('popup_enabled', True))
            self.chk_highlight.setChecked(
                state.get('highlight_enabled', False))
            op = state.get('highlight_op', '>')
            oi = self.cbo_hl_op.findText(op)
            if oi >= 0:
                self.cbo_hl_op.setCurrentIndex(oi)
            self.txt_hl_val.setText(
                state.get('highlight_val', '1'))

            # Style
            if self.geom_key == 'point':
                ic = state.get('icon_color', '#FF4444')
                self.btn_icon_clr.setText(ic)
                self.btn_icon_clr.setStyleSheet(
                    f"background-color:{ic}")
                self.spn_icon_scale.setValue(
                    state.get('icon_scale', 1.0))
            elif self.geom_key == 'line':
                lc = state.get('line_color', '#0000FF')
                self.btn_line_clr.setText(lc)
                self.btn_line_clr.setStyleSheet(
                    f"background-color:{lc}")
                self.spn_line_w.setValue(
                    state.get('line_width', 2))
            else:
                bc = state.get('border_color', '#FF0000')
                self.btn_border.setText(bc)
                self.btn_border.setStyleSheet(
                    f"background-color:{bc}")
                self.spn_bw.setValue(
                    state.get('border_width', 2))
                fc = state.get('fill_color', '#00FF00')
                self.btn_fill.setText(fc)
                self.btn_fill.setStyleSheet(
                    f"background-color:{fc}")
                self.sld_op.setValue(
                    state.get('fill_opacity', 50))

            # Table with ★ column
            self.tbl_fields.setRowCount(0)
            saved_fnames = set()
            for sf in state.get('fields', []):
                fn = sf.get('field', '')
                if fn not in fields:
                    continue
                saved_fnames.add(fn)
                r = self.tbl_fields.rowCount()
                self.tbl_fields.insertRow(r)
                chk = QCheckBox()
                chk.setChecked(sf.get('checked', True))
                chk.stateChanged.connect(self._notify)
                self.tbl_fields.setCellWidget(r, 0, chk)
                self.tbl_fields.setItem(
                    r, 1, QTableWidgetItem(fn))
                self.tbl_fields.setItem(
                    r, 2, QTableWidgetItem(
                        sf.get('alias', fn)))
                self.tbl_fields.setItem(
                    r, 3, QTableWidgetItem(
                        sf.get('unit', '')))
                hl = QCheckBox()
                hl.setChecked(sf.get('highlight', False))
                hl.stateChanged.connect(self._notify)
                self.tbl_fields.setCellWidget(r, 4, hl)
            for fn in fields:
                if fn not in saved_fnames:
                    self._add_field_row(fn)
        else:
            self.chk_popup.setChecked(True)
            self.chk_highlight.setChecked(False)
            self.chk_f1.setChecked(True)
            self.chk_f2.setChecked(False)
            self.tbl_fields.setRowCount(0)
            for fn in fields:
                self._add_field_row(fn)

        self.cbo_f1.setEnabled(self.chk_f1.isChecked())
        self.cbo_f2.setEnabled(self.chk_f2.isChecked())

        # Unblock
        for w in [self.tbl_fields, self.cbo_f1, self.cbo_f2,
                  self.chk_f1, self.chk_f2, self.txt_sep,
                  self.spn_size, self.txt_header,
                  self.chk_popup, self.chk_highlight,
                  self.cbo_hl_op, self.txt_hl_val]:
            w.blockSignals(False)

        self._loading = False
        self._notify()

    def _add_field_row(self, fn, checked=True, highlight=False):
        """Add a row to the fields table."""
        r = self.tbl_fields.rowCount()
        self.tbl_fields.insertRow(r)
        chk = QCheckBox()
        chk.setChecked(checked)
        chk.stateChanged.connect(self._notify)
        self.tbl_fields.setCellWidget(r, 0, chk)
        self.tbl_fields.setItem(
            r, 1, QTableWidgetItem(fn))
        self.tbl_fields.setItem(
            r, 2, QTableWidgetItem(fn))
        self.tbl_fields.setItem(
            r, 3, QTableWidgetItem(""))
        hl = QCheckBox()
        hl.setChecked(highlight)
        hl.stateChanged.connect(self._notify)
        self.tbl_fields.setCellWidget(r, 4, hl)

    def _notify(self):
        if self._loading:
            return
        self._update_name_label()
        if self._on_change:
            self._on_change()

    def _update_name_label(self):
        name = self.build_name_text()
        clr = self.btn_name_clr.text()
        sz = self.spn_size.value()
        if name:
            self.lbl_name_preview.setText(
                f"<b>Preview:</b> "
                f"<span style='color:{clr};font-size:{sz}px'>"
                f"{name}</span>")
        else:
            self.lbl_name_preview.setText(
                "<b>Preview:</b> <i>(không hiển thị)</i>")

    # ── Public methods ────────────────────────────────

    def build_name_text(self):
        f1 = (self.cbo_f1.currentText()
              if self.chk_f1.isChecked() else '')
        f2 = (self.cbo_f2.currentText()
              if self.chk_f2.isChecked() else '')
        sep = self.txt_sep.text()
        v1 = str(self._sample_data.get(f1, '')) if f1 else ''
        v2 = str(self._sample_data.get(f2, '')) if f2 else ''
        if v1 in ('NULL', 'None', ''):
            v1 = ''
        if v2 in ('NULL', 'None', ''):
            v2 = ''
        if v1 and v2:
            return f"{v1}{sep}{v2}"
        return v1 or v2 or ''

    def set_layers(self, layers_info):
        self._save_current()
        self._layer_keys = []
        self._layer_fields = {}
        self._layer_samples = {}

        for key, name, fields, sample in layers_info:
            self._layer_keys.append(key)
            self._layer_names[key] = name
            self._layer_fields[key] = fields
            self._layer_samples[key] = sample

        for old in list(self._layer_states.keys()):
            if old not in self._layer_keys:
                del self._layer_states[old]
                if old in self._layer_names:
                    del self._layer_names[old]

        prev_key = self._current_key
        self.cbo_layer.blockSignals(True)
        self.cbo_layer.clear()
        for key in self._layer_keys:
            self.cbo_layer.addItem(self._layer_names[key])
        if prev_key in self._layer_keys:
            idx = self._layer_keys.index(prev_key)
            self.cbo_layer.setCurrentIndex(idx)
        self.cbo_layer.blockSignals(False)

        if self._layer_keys:
            idx = self.cbo_layer.currentIndex()
            if idx < 0:
                idx = 0
            self._current_key = self._layer_keys[idx]
            self._load_layer(self._current_key)
        else:
            self._current_key = None
            self._sample_data = {}
            self._fields = []
            self.cbo_f1.clear()
            self.cbo_f2.clear()
            self.tbl_fields.setRowCount(0)
            self._notify()

    def get_all_layer_configs(self):
        self._save_current()
        result = []
        for key in self._layer_keys:
            state = self._layer_states.get(key)
            fields = self._layer_fields.get(key, [])
            if state:
                cfg = self._state_to_config(state)
            else:
                cfg = self._default_config(fields)
            result.append((key, cfg))
        return result

    def _state_to_config(self, state):
        name_cfg = {
            'field1': state.get('f1_field', ''),
            'field1_enabled': state.get('f1_checked', True),
            'field2': state.get('f2_field', ''),
            'field2_enabled': state.get('f2_checked', False),
            'separator': state.get('separator', ' - '),
            'font_size': state.get('font_size', 12),
            'font_color': state.get('name_color', '#000000'),
        }
        desc = []
        for i, sf in enumerate(state.get('fields', [])):
            if sf.get('checked', True):
                desc.append({
                    'field': sf.get('field', ''),
                    'alias': sf.get('alias', ''),
                    'suffix': sf.get('unit', ''),
                    'order': i,
                })

        # Build highlight rules from ★-marked fields
        hl_rules = []
        if state.get('highlight_enabled', False):
            op = state.get('highlight_op', '>')
            val = state.get('highlight_val', '1')
            for sf in state.get('fields', []):
                if sf.get('highlight', False):
                    hl_rules.append({
                        'field': sf.get('field', ''),
                        'operator': op,
                        'value': val,
                        'text_color': '#0000CC',
                        'bg_color': '#FFE0E0',
                        'bold': True,
                    })

        cfg = {
            'name_fields': name_cfg,
            'description_fields': desc,
            'popup_enabled': state.get('popup_enabled', True),
            'header': {
                'title': state.get(
                    'header_title', 'Thông tin'),
                'bg_color': state.get(
                    'header_bg', '#1B5E20'),
                'text_color': '#FFFFFF',
                'bold': True,
                'font_size': 14,
            },
            'row_highlights': {
                'enabled': bool(hl_rules),
                'rules': hl_rules,
            },
        }
        if self.geom_key == 'point':
            cfg['point_style'] = {
                'icon_color': state.get(
                    'icon_color', '#FF4444'),
                'icon_scale': state.get('icon_scale', 1.0),
            }
        elif self.geom_key == 'line':
            cfg['line_style'] = {
                'line_color': state.get(
                    'line_color', '#0000FF'),
                'line_width': state.get('line_width', 2),
            }
        else:
            cfg['polygon_style'] = {
                'border_color': state.get(
                    'border_color', '#FF0000'),
                'border_width': state.get(
                    'border_width', 2),
                'fill_color': state.get(
                    'fill_color', '#00FF00'),
                'fill_opacity': state.get(
                    'fill_opacity', 50),
            }
        return cfg

    def _default_config(self, fields):
        desc = [{'field': f, 'alias': f, 'suffix': '',
                 'order': i}
                for i, f in enumerate(fields)]
        cfg = {
            'name_fields': {
                'field1': fields[0] if fields else '',
                'field1_enabled': True,
                'field2': '',
                'field2_enabled': False,
                'separator': ' - ',
                'font_size': 12,
                'font_color': '#000000',
            },
            'description_fields': desc,
            'popup_enabled': True,
            'header': {
                'title': 'Thông tin',
                'bg_color': '#1B5E20',
                'text_color': '#FFFFFF',
                'bold': True,
                'font_size': 14,
            },
            'row_highlights': {
                'enabled': False, 'rules': []},
        }
        if self.geom_key == 'point':
            cfg['point_style'] = {
                'icon_color': '#FF4444',
                'icon_scale': 1.0,
            }
        elif self.geom_key == 'line':
            cfg['line_style'] = {
                'line_color': '#0000FF',
                'line_width': 2,
            }
        else:
            cfg['polygon_style'] = {
                'border_color': '#FF0000',
                'border_width': 2,
                'fill_color': '#00FF00',
                'fill_opacity': 50,
            }
        return cfg

    def get_config(self):
        """Return config for the currently displayed layer."""
        name_cfg = {
            'field1': self.cbo_f1.currentText(),
            'field1_enabled': self.chk_f1.isChecked(),
            'field2': self.cbo_f2.currentText(),
            'field2_enabled': self.chk_f2.isChecked(),
            'separator': self.txt_sep.text(),
            'font_size': self.spn_size.value(),
            'font_color': self.btn_name_clr.text(),
        }
        desc = []
        hl_rules = []
        for i in range(self.tbl_fields.rowCount()):
            chk = self.tbl_fields.cellWidget(i, 0)
            if chk and chk.isChecked():
                fi = self.tbl_fields.item(i, 1)
                ai = self.tbl_fields.item(i, 2)
                si = self.tbl_fields.item(i, 3)
                if fi and ai:
                    desc.append({
                        'field': fi.text(),
                        'alias': ai.text(),
                        'suffix': si.text() if si else '',
                        'order': i,
                    })
            # Highlight
            hl = self.tbl_fields.cellWidget(i, 4)
            if (hl and hl.isChecked() and  # noqa: W504
                    self.chk_highlight.isChecked()):
                fi = self.tbl_fields.item(i, 1)
                if fi:
                    hl_rules.append({
                        'field': fi.text(),
                        'operator': self.cbo_hl_op.currentText(),
                        'value': self.txt_hl_val.text(),
                        'text_color': '#0000CC',
                        'bg_color': '#FFE0E0',
                        'bold': True,
                    })
        cfg = {
            'name_fields': name_cfg,
            'description_fields': desc,
            'popup_enabled': self.chk_popup.isChecked(),
            'header': {
                'title': self.txt_header.text(),
                'bg_color': self.btn_hdr_bg.text(),
                'text_color': '#FFFFFF',
                'bold': True,
                'font_size': 14,
            },
            'row_highlights': {
                'enabled': bool(hl_rules),
                'rules': hl_rules,
            },
        }
        if self.geom_key == 'point':
            cfg['point_style'] = {
                'icon_color': self.btn_icon_clr.text(),
                'icon_scale': self.spn_icon_scale.value(),
            }
        elif self.geom_key == 'line':
            cfg['line_style'] = {
                'line_color': self.btn_line_clr.text(),
                'line_width': self.spn_line_w.value(),
            }
        else:
            cfg['polygon_style'] = {
                'border_color': self.btn_border.text(),
                'border_width': self.spn_bw.value(),
                'fill_color': self.btn_fill.text(),
                'fill_opacity': self.sld_op.value(),
            }
        return cfg

    def update_labels(self, lang):
        is_vi = lang == 'vi'
        for gp in self.findChildren(QGroupBox):
            t = gp.title()
            if t == 'Layer':
                pass
            elif 'Label' in t or 'Nhãn' in t:
                gp.setTitle(
                    "Nhãn (Label)" if is_vi else "Label")
            elif 'Popup' in t and 'Header' not in t:
                gp.setTitle(
                    "Popup (Trường dữ liệu)" if is_vi
                    else "Popup (Fields)")
            elif 'Style' in t or 'Kiểu' in t:
                gp.setTitle(
                    "Kiểu dáng" if is_vi else "Style")
            elif 'Header' in t or 'Tiêu đề' in t:
                gp.setTitle(
                    "Tiêu đề Popup" if is_vi
                    else "Popup Header")
        for lbl in self.findChildren(QLabel):
            if lbl.text() in ('Chọn layer:',
                              'Select layer:'):
                lbl.setText(
                    "Chọn layer:" if is_vi
                    else "Select layer:")
        self.chk_popup.setText(
            "Bật Popup thông tin" if is_vi
            else "Enable Popup Info")
        self.chk_highlight.setText(
            "Highlight:" if is_vi
            else "Highlight:")

    # ── Private helpers ───────────────────────────────

    def _pick(self, btn):
        c = QColorDialog.getColor(QColor(btn.text()))
        if c.isValid():
            btn.setText(c.name())
            btn.setStyleSheet(
                f"background-color:{c.name()}")

    def _check_all(self):
        for i in range(self.tbl_fields.rowCount()):
            chk = self.tbl_fields.cellWidget(i, 0)
            if chk:
                chk.setChecked(True)

    def _uncheck_all(self):
        for i in range(self.tbl_fields.rowCount()):
            chk = self.tbl_fields.cellWidget(i, 0)
            if chk:
                chk.setChecked(False)

    def _move_up(self):
        row = self.tbl_fields.currentRow()
        if row <= 0:
            return
        self._swap_rows(row, row - 1)
        self.tbl_fields.setCurrentCell(row - 1, 0)
        self._notify()

    def _move_down(self):
        row = self.tbl_fields.currentRow()
        if row < 0 or row >= self.tbl_fields.rowCount() - 1:
            return
        self._swap_rows(row, row + 1)
        self.tbl_fields.setCurrentCell(row + 1, 0)
        self._notify()

    def _swap_rows(self, r1, r2):
        self.tbl_fields.blockSignals(True)
        chk1 = self.tbl_fields.cellWidget(r1, 0)
        chk2 = self.tbl_fields.cellWidget(r2, 0)
        c1 = chk1.isChecked() if chk1 else True
        c2 = chk2.isChecked() if chk2 else True
        hl1 = self.tbl_fields.cellWidget(r1, 4)
        hl2 = self.tbl_fields.cellWidget(r2, 4)
        h1 = hl1.isChecked() if hl1 else False
        h2 = hl2.isChecked() if hl2 else False
        vals1 = [self.tbl_fields.item(r1, c).text()
                 if self.tbl_fields.item(r1, c) else ''
                 for c in range(1, 4)]
        vals2 = [self.tbl_fields.item(r2, c).text()
                 if self.tbl_fields.item(r2, c) else ''
                 for c in range(1, 4)]
        new_c1 = QCheckBox()
        new_c1.setChecked(c2)
        new_c1.stateChanged.connect(self._notify)
        new_c2 = QCheckBox()
        new_c2.setChecked(c1)
        new_c2.stateChanged.connect(self._notify)
        self.tbl_fields.setCellWidget(r1, 0, new_c1)
        self.tbl_fields.setCellWidget(r2, 0, new_c2)
        new_h1 = QCheckBox()
        new_h1.setChecked(h2)
        new_h1.stateChanged.connect(self._notify)
        new_h2 = QCheckBox()
        new_h2.setChecked(h1)
        new_h2.stateChanged.connect(self._notify)
        self.tbl_fields.setCellWidget(r1, 4, new_h1)
        self.tbl_fields.setCellWidget(r2, 4, new_h2)
        for c in range(1, 4):
            self.tbl_fields.setItem(
                r1, c, QTableWidgetItem(vals2[c - 1]))
            self.tbl_fields.setItem(
                r2, c, QTableWidgetItem(vals1[c - 1]))
        self.tbl_fields.blockSignals(False)


# ── Main Merge Tab widget ─────────────────────────────

class MergeTabWidget(QWidget):
    """Complete Tab 3 for merging multiple SHP → 1 KML / KMZ."""

    GEOM_LABELS = {
        QgsWkbTypes.PointGeometry: ('point', '🔵 Điểm (Point)'),
        QgsWkbTypes.LineGeometry: ('line', '🟢 Đường (Line)'),
        QgsWkbTypes.PolygonGeometry: (
            'polygon', '🟡 Vùng (Polygon)'),
    }

    def __init__(self, lang='vi', parent=None):
        super().__init__(parent)
        self.lang = lang
        self._shp_paths = []
        self._shp_infos = []
        self._initialized = False
        self._bg_img = os.path.join(
            os.path.dirname(__file__),
            "google_maps_satellite_bg.png")
        self._placeholder_ly = QVBoxLayout(self)
        self._placeholder_ly.addWidget(
            QLabel("⏳ Nhấn vào tab để bắt đầu..."))
        self._placeholder_ly.addStretch()

    def ensure_initialized(self):
        if self._initialized:
            return
        self._initialized = True
        while self._placeholder_ly.count():
            item = self._placeholder_ly.takeAt(0)
            w = item.widget()
            if w:
                w.deleteLater()
        root_ly = self._placeholder_ly
        main_split = QSplitter(Qt.Horizontal)

        # ══════ LEFT ══════
        left = QWidget()
        left_ly = QVBoxLayout(left)
        left_ly.setContentsMargins(0, 0, 0, 0)

        gp_files = QGroupBox("1. Chọn file SHP")
        fl = QVBoxLayout()
        btn_row = QHBoxLayout()
        self.btn_add = QPushButton("➕ Thêm SHP")
        self.btn_add.clicked.connect(self._add_shp)
        self.btn_add_canvas = QPushButton("🗺 Lấy từ QGIS")
        self.btn_add_canvas.setStyleSheet(
            "QPushButton{background:#1565C0;color:#fff;"
            "font-weight:bold;padding:4px 10px;"
            "border-radius:3px}"
            "QPushButton:hover{background:#1976D2}")
        self.btn_add_canvas.clicked.connect(
            self._add_from_canvas)
        self.btn_remove = QPushButton("➖ Xóa")
        self.btn_remove.clicked.connect(self._remove_shp)
        self.btn_clear = QPushButton("🗑 Xóa hết")
        self.btn_clear.clicked.connect(self._clear_shp)
        btn_row.addWidget(self.btn_add)
        btn_row.addWidget(self.btn_add_canvas)
        btn_row.addWidget(self.btn_remove)
        btn_row.addWidget(self.btn_clear)
        btn_row.addStretch()
        fl.addLayout(btn_row)

        self.tbl_files = QTableWidget(0, 3)
        self.tbl_files.setHorizontalHeaderLabels(
            ["File", "Geometry", "Path"])
        self.tbl_files.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.Stretch)
        self.tbl_files.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.tbl_files.horizontalHeader().setSectionResizeMode(
            2, QHeaderView.Stretch)
        self.tbl_files.setMaximumHeight(100)
        self.tbl_files.clicked.connect(self._on_file_clicked)
        fl.addWidget(self.tbl_files)
        self.lbl_summary = QLabel("")
        self.lbl_summary.setStyleSheet(
            "color:#555;font-size:11px")
        fl.addWidget(self.lbl_summary)
        gp_files.setLayout(fl)
        left_ly.addWidget(gp_files)

        gp_cfg = QGroupBox(
            "2. Thiết lập theo loại đối tượng")
        cfg_ly = QVBoxLayout()
        self.geom_tabs = QTabWidget()
        self.panel_point = _GeomConfigPanel(
            'point', on_change=self._refresh_preview)
        self.panel_line = _GeomConfigPanel(
            'line', on_change=self._refresh_preview)
        self.panel_poly = _GeomConfigPanel(
            'polygon', on_change=self._refresh_preview)
        self.geom_tabs.addTab(
            self.panel_point, "🔵 Điểm (Point)")
        self.geom_tabs.addTab(
            self.panel_line, "🟢 Đường (Line)")
        self.geom_tabs.addTab(
            self.panel_poly, "🟡 Vùng (Polygon)")
        self.geom_tabs.currentChanged.connect(
            lambda: self._refresh_preview())
        cfg_ly.addWidget(self.geom_tabs)
        gp_cfg.setLayout(cfg_ly)
        left_ly.addWidget(gp_cfg, 1)
        main_split.addWidget(left)

        # ══════ RIGHT — LIVE PREVIEW ══════
        right = QWidget()
        r_ly = QVBoxLayout(right)
        r_ly.addWidget(QLabel("<b>🖼 LIVE PREVIEW</b>"))

        self._preview_frame = QFrame()
        self._preview_frame.setMinimumHeight(180)
        self._preview_frame.setFrameStyle(QFrame.StyledPanel)
        bg_path = self._bg_img.replace('\\', '/')
        self._preview_frame.setStyleSheet(
            f"background-image: url({bg_path});"
            "background-position: center;")
        self._preview_frame.paintEvent = self._paint_preview
        r_ly.addWidget(self._preview_frame)

        r_ly.addWidget(QLabel("<b>Popup Preview:</b>"))
        self._html_preview = QTextEdit()
        self._html_preview.setReadOnly(True)
        r_ly.addWidget(self._html_preview, 1)

        self.btn_export = QPushButton("🚀 XUẤT KML / KMZ")
        self.btn_export.setMinimumHeight(45)
        self.btn_export.setStyleSheet(
            "background:#1B5E20;color:white;"
            "font-weight:bold;border-radius:5px;"
            "font-size:14px")
        self.btn_export.clicked.connect(self._export)
        r_ly.addWidget(self.btn_export)

        main_split.addWidget(right)
        main_split.setStretchFactor(0, 6)
        main_split.setStretchFactor(1, 4)
        root_ly.addWidget(main_split)

    # ── Preview ───────────────────────────────────────

    def _active_panel(self):
        idx = self.geom_tabs.currentIndex()
        return [self.panel_point, self.panel_line,
                self.panel_poly][idx]

    def _refresh_preview(self):
        if not self._initialized:
            return
        self._preview_frame.update()
        self._update_popup_preview()

    def _update_popup_preview(self):
        panel = self._active_panel()
        if not panel.chk_popup.isChecked():
            self._html_preview.setHtml(
                "<div style='text-align:center;"
                "color:#888;padding:20px'>"
                "<b>Popup đã tắt</b><br>"
                "Layer này chỉ hiển thị style.</div>")
            return
        if not panel._sample_data:
            self._html_preview.setHtml(
                "<i style='color:#999'>"
                "Chưa có dữ liệu mẫu.<br>"
                "Thêm file SHP để xem preview.</i>")
            return
        try:
            cfg = panel.get_config()
            html = HtmlTemplateBuilder(cfg).build(
                panel._sample_data)
            self._html_preview.setHtml(
                f"<div align='center'>{html}</div>")
        except Exception:
            self._html_preview.setHtml(
                "<i style='color:#c00'>Preview error</i>")

    def _paint_preview(self, event):
        panel = self._active_panel()
        painter = QPainter(self._preview_frame)
        painter.setRenderHint(QPainter.Antialiasing)
        w = self._preview_frame.width()
        h = self._preview_frame.height()
        cx, cy = w // 2, h // 2
        gk = panel.geom_key
        if gk == 'polygon':
            self._paint_polygon(painter, panel, cx, cy)
        elif gk == 'line':
            self._paint_line(painter, panel, w, h)
        else:
            self._paint_point(painter, panel, cx, cy)
        try:
            name = panel.build_name_text()
            if name:
                clr = QColor(panel.btn_name_clr.text())
                sz = panel.spn_size.value()
                font = painter.font()
                font.setPointSize(sz)
                painter.setFont(font)
                painter.setPen(clr)
                painter.drawText(
                    QRect(10, 10, w - 20, h - 20),
                    Qt.AlignCenter, name)
        except Exception:
            pass

    def _paint_polygon(self, painter, panel, cx, cy):
        b_c = QColor(panel.btn_border.text())
        f_c = QColor(panel.btn_fill.text())
        bw = panel.spn_bw.value()
        f_c.setAlpha(int(panel.sld_op.value() * 2.55))
        painter.setPen(QPen(b_c, bw))
        painter.setBrush(QBrush(f_c))
        dx, dy = 80, 55
        pts = [
            QPoint(cx, cy - dy), QPoint(cx + dx, cy),
            QPoint(cx, cy + dy), QPoint(cx - dx, cy)]
        painter.drawPolygon(QPolygon(pts))

    def _paint_line(self, painter, panel, w, h):
        clr = QColor(panel.btn_line_clr.text())
        lw = panel.spn_line_w.value()
        painter.setPen(QPen(clr, lw))
        path = QPainterPath()
        path.moveTo(w * 0.1, h * 0.7)
        path.cubicTo(w * 0.3, h * 0.2,
                     w * 0.6, h * 0.8,
                     w * 0.9, h * 0.3)
        painter.drawPath(path)

    def _paint_point(self, painter, panel, cx, cy):
        clr = QColor(panel.btn_icon_clr.text())
        scale = panel.spn_icon_scale.value()
        r = int(8 * scale)
        painter.setPen(QPen(clr.darker(120), 2))
        painter.setBrush(QBrush(clr))
        for dx, dy in [(0, 0), (-40, -25), (35, -20),
                       (-25, 30), (45, 25)]:
            painter.drawEllipse(
                QPoint(cx + dx, cy + dy), r, r)

    # ── Public ────────────────────────────────────────

    def refresh_geom_tabs(self):
        self.ensure_initialized()
        self._refresh_fields()

    def update_labels(self, lang):
        self.lang = lang
        if not self._initialized:
            return
        is_vi = lang == 'vi'
        self.btn_add.setText(
            "➕ Thêm SHP" if is_vi else "➕ Add SHP")
        self.btn_add_canvas.setText(
            "🗺 Lấy từ QGIS" if is_vi
            else "🗺 From QGIS")
        self.btn_remove.setText(
            "➖ Xóa" if is_vi else "➖ Remove")
        self.btn_clear.setText(
            "🗑 Xóa hết" if is_vi else "🗑 Clear")
        self.btn_export.setText(
            "🚀 XUẤT KML / KMZ" if is_vi
            else "🚀 EXPORT KML / KMZ")
        for gp in self.findChildren(QGroupBox):
            t = gp.title()
            if 'SHP' in t or 'file' in t.lower():
                gp.setTitle(
                    "1. Chọn file SHP" if is_vi
                    else "1. Select SHP Files")
            elif 'loại' in t or 'geometry' in t.lower():
                gp.setTitle(
                    "2. Thiết lập theo loại đối tượng"
                    if is_vi
                    else "2. Config by Geometry Type")
        self.panel_point.update_labels(lang)
        self.panel_line.update_labels(lang)
        self.panel_poly.update_labels(lang)

    # ── Private ───────────────────────────────────────

    def _on_file_clicked(self, index):
        row = index.row()
        if row < 0 or row >= len(self._shp_infos):
            return
        _, gk = self._shp_infos[row]
        tab_map = {'point': 0, 'line': 1, 'polygon': 2}
        self.geom_tabs.setCurrentIndex(
            tab_map.get(gk, 0))

    def _add_shp(self):
        paths, _ = QFileDialog.getOpenFileNames(
            self,
            "Chọn file SHP" if self.lang == 'vi'
            else "Select SHP",
            "", "Shapefile (*.shp)")
        if not paths:
            return
        for p in paths:
            if p in self._shp_paths:
                continue
            bn = os.path.splitext(
                os.path.basename(p))[0]
            layer = QgsVectorLayer(p, bn, 'ogr')
            if not layer.isValid():
                continue
            gt = layer.geometryType()
            info = self.GEOM_LABELS.get(gt)
            if info is None:
                continue
            gk, glabel = info
            self._shp_paths.append(p)
            self._shp_infos.append((bn, gk))
            row = self.tbl_files.rowCount()
            self.tbl_files.insertRow(row)
            item_name = QTableWidgetItem(bn)
            item_name.setFlags(
                item_name.flags() & ~Qt.ItemIsEditable)
            self.tbl_files.setItem(row, 0, item_name)
            item_geom = QTableWidgetItem(glabel)
            item_geom.setFlags(
                item_geom.flags() & ~Qt.ItemIsEditable)
            self.tbl_files.setItem(row, 1, item_geom)
            item_path = QTableWidgetItem(p)
            item_path.setFlags(
                item_path.flags() & ~Qt.ItemIsEditable)
            self.tbl_files.setItem(row, 2, item_path)
        self._update_summary()
        self._refresh_fields()

    def _add_from_canvas(self):
        added = 0
        for layer in (
                QgsProject.instance().mapLayers().values()):
            if not isinstance(layer, QgsVectorLayer):
                continue
            src = layer.source()
            if not src:
                continue
            base_src = (src.split('|')[0]
                        if '|' in src else src)
            if not os.path.exists(base_src):
                continue
            if src in self._shp_paths:
                continue
            gt = layer.geometryType()
            info = self.GEOM_LABELS.get(gt)
            if info is None:
                continue
            gk, glabel = info
            bn = layer.name()
            self._shp_paths.append(src)
            self._shp_infos.append((bn, gk))
            row = self.tbl_files.rowCount()
            self.tbl_files.insertRow(row)
            item_name = QTableWidgetItem(bn)
            item_name.setFlags(
                item_name.flags() & ~Qt.ItemIsEditable)
            self.tbl_files.setItem(row, 0, item_name)
            item_geom = QTableWidgetItem(glabel)
            item_geom.setFlags(
                item_geom.flags() & ~Qt.ItemIsEditable)
            self.tbl_files.setItem(row, 1, item_geom)
            item_path = QTableWidgetItem(
                os.path.basename(base_src))
            item_path.setFlags(
                item_path.flags() & ~Qt.ItemIsEditable)
            item_path.setToolTip(src)
            self.tbl_files.setItem(row, 2, item_path)
            added += 1
        if added == 0:
            QMessageBox.information(
                self, "Info",
                "Không tìm thấy layer nào!"
                if self.lang == 'vi'
                else "No layers found!")
        else:
            self._update_summary()
            self._refresh_fields()

    def _remove_shp(self):
        row = self.tbl_files.currentRow()
        if row < 0:
            return
        self._shp_paths.pop(row)
        self._shp_infos.pop(row)
        self.tbl_files.removeRow(row)
        self._update_summary()
        self._refresh_fields()

    def _clear_shp(self):
        self._shp_paths.clear()
        self._shp_infos.clear()
        self.tbl_files.setRowCount(0)
        self._update_summary()
        self._refresh_fields()

    def _refresh_fields(self):
        pt_layers, ln_layers, pg_layers = [], [], []
        for path, (bn, gk) in zip(
                self._shp_paths, self._shp_infos):
            layer = QgsVectorLayer(path, 'tmp', 'ogr')
            if not layer.isValid():
                continue
            fnames = sorted(
                f.name() for f in layer.fields())
            sample = {}
            feat = next(layer.getFeatures(), None)
            if feat:
                sample = {
                    f.name(): feat[f.name()]
                    for f in feat.fields()}
            info = (path, bn, fnames, sample)
            if gk == 'point':
                pt_layers.append(info)
            elif gk == 'line':
                ln_layers.append(info)
            else:
                pg_layers.append(info)
        self.panel_point.set_layers(pt_layers)
        self.panel_line.set_layers(ln_layers)
        self.panel_poly.set_layers(pg_layers)

    def _update_summary(self):
        n = len(self._shp_paths)
        pts = sum(1 for _, g in self._shp_infos
                  if g == 'point')
        lns = sum(1 for _, g in self._shp_infos
                  if g == 'line')
        pgs = sum(1 for _, g in self._shp_infos
                  if g == 'polygon')
        self.lbl_summary.setText(
            f"📂 {n} files  |  🔵 {pts} Point  "
            f"🟢 {lns} Line  🟡 {pgs} Polygon")

    def _export(self):
        if not self._shp_paths:
            QMessageBox.warning(
                self,
                "Lỗi" if self.lang == 'vi' else "Error",
                "Chưa thêm file SHP nào!"
                if self.lang == 'vi'
                else "No SHP files added!")
            return
        out_path, _ = QFileDialog.getSaveFileName(
            self,
            "Lưu KML / KMZ" if self.lang == 'vi'
            else "Save KML / KMZ",
            "", "KML (*.kml);;KMZ (*.kmz)")
        if not out_path:
            return

        panels = {
            'point': self.panel_point,
            'line': self.panel_line,
            'polygon': self.panel_poly,
        }
        path_cfg = {}
        for gk, panel in panels.items():
            for key, cfg in panel.get_all_layer_configs():
                path_cfg[key] = cfg

        layer_configs = []
        for path in self._shp_paths:
            cfg = path_cfg.get(path)
            if cfg:
                layer_configs.append((path, cfg))

        if not layer_configs:
            QMessageBox.warning(
                self,
                "Lỗi" if self.lang == 'vi' else "Error",
                "Không có layer nào để xuất!")
            return

        fmt = ('kmz' if out_path.lower().endswith('.kmz')
               else 'kml')
        builder = MergeKmlBuilder()
        ok, msg = builder.build(
            layer_configs, out_path, fmt)

        if ok:
            title = ("Thành công" if self.lang == 'vi'
                     else "Success")
            QMessageBox.information(self, title, msg)
        else:
            title = ("Lỗi" if self.lang == 'vi'
                     else "Error")
            QMessageBox.warning(self, title, msg)
