# -*- coding: utf-8 -*-
"""Map Layout dialog - pick an area, configure insets. Simplified UI.

Changes:
- Removed "Layers to include" section (always loads water/sea/points)
- Removed "Focus on area" dropdown (always uses surround mode)
- Hide irrelevant widgets based on area source selection (HIDDEN, not disabled)
- Supports Admin 0-3 for global use
- Auto-labels all surrounding features
- GEE asset (advanced) option hides admin unit selectors completely
"""

import os
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QColor
from qgis.PyQt.QtWidgets import (
    QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QStackedWidget, QComboBox,
    QLineEdit, QCheckBox, QGroupBox, QDialogButtonBox, QLabel, QScrollArea,
    QWidget, QPushButton,
)
from qgis.core import QgsProject, QgsVectorLayer, QgsWkbTypes
from . import adminmap
from .dialogs.help_texts import help_button
from .layout_builder import FILL_STYLES
try:
    from qgis.gui import QgsColorButton
    _HAVE_COLOR_BTN = True
except (ImportError, ModuleNotFoundError, RuntimeError):  # GEE/Import
    _HAVE_COLOR_BTN = False

BOUNDARY_DIR = os.path.join(os.path.dirname(__file__), "data", "boundaries")
_CONTEXT_NAMES = ("Sea", "Waterbody")


def bundled_boundaries():
    out = []
    if os.path.isdir(BOUNDARY_DIR):
        for fn in sorted(os.listdir(BOUNDARY_DIR)):
            if fn.lower().endswith(".shp"):
                name = os.path.splitext(fn)[0]
                if name not in _CONTEXT_NAMES:
                    out.append((name, os.path.join(BOUNDARY_DIR, fn)))
    return out


class AdminMapDialog(QDialog):
    def __init__(self, iface, parent=None):
        super().__init__(parent)
        self.iface = iface
        self.setWindowTitle("Map Layout - build a map for an area")
        self.setMinimumWidth(600)
        self.insets = []
        self._build()

    def _build(self):
        root = QVBoxLayout(self)
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        content = QWidget()
        v = QVBoxLayout(content)

        # ===== AREA SOURCE =====
        gb_source = QGroupBox("Select Area")
        g = QVBoxLayout(gb_source)
        g.setContentsMargins(10, 14, 10, 8)
        g.setSpacing(7)

        h = QHBoxLayout()
        h.addWidget(QLabel("Area source:"))
        self.cmb_source = QComboBox()
        self.cmb_source.addItems([
            "Administrative unit (bundled)",
            "GEE asset (advanced)",
            "Current map canvas extent",
            "Drawn polygon / AOI from Tool 1",
        ])
        self.cmb_source.currentIndexChanged.connect(self._on_source)
        h.addWidget(self.cmb_source, 1)
        g.addLayout(h)

        # Stacked widget holds the three option groups;
        # only the active page is ever visible — completely hides
        # the others (no ghost space, no disabled look).
        self.stk_aoi = QStackedWidget()

        # --- Page 0: Admin unit selectors ---
        w_admin = QWidget()
        ga = QFormLayout(w_admin)
        ga.setContentsMargins(0, 0, 0, 0)
        ga.setVerticalSpacing(7)

        self.cmb_prov = QComboBox()
        self.cmb_dist = QComboBox()
        self.cmb_comm = QComboBox()

        if adminmap._has_cambodia:
            lbl1, lbl2, lbl3 = "Province:", "District:", "Commune:"
        elif adminmap._has_global:
            lbl1, lbl2, lbl3 = "Admin 1 (State/Province):", "Admin 2 (District):", "Admin 3 (Commune):"
        else:
            lbl1, lbl2, lbl3 = "Level 1:", "Level 2:", "Level 3:"

        self.cmb_prov.addItem("(choose " + lbl1.replace(":", "").lower() + ")", None)
        for label, code, name in adminmap.read_units(adminmap.ADMIN_LEVELS[0]):
            self.cmb_prov.addItem(label, (code, name))

        self.cmb_dist.addItem("(whole " + lbl1.replace(":", "").lower() + ")", None)
        self.cmb_comm.addItem("(whole " + lbl2.replace(":", "").lower() + ")", None)

        self.cmb_prov.currentIndexChanged.connect(self._on_prov)
        self.cmb_dist.currentIndexChanged.connect(self._on_dist)

        ga.addRow(lbl1, self.cmb_prov)
        ga.addRow(lbl2, self.cmb_dist)
        ga.addRow(lbl3, self.cmb_comm)
        self.stk_aoi.addWidget(w_admin)

        # --- Page 1: GEE Asset selector ---
        w_gee = QWidget()
        gg = QFormLayout(w_gee)
        gg.setContentsMargins(0, 0, 0, 0)
        gg.setVerticalSpacing(7)

        self.cmb_gee_asset = QComboBox()
        self.cmb_gee_asset.addItem("(select GEE asset)", "")
        from . import gee_utils as gu
        for asset_path in getattr(gu, 'AOI_ASSETS', []):
            name = asset_path.split("/")[-1] if "/" in asset_path else asset_path
            self.cmb_gee_asset.addItem(name, asset_path)
        self.cmb_gee_field = QComboBox()
        self.cmb_gee_field.addItem("(select field)", "")
        self.cmb_gee_value = QComboBox()
        self.cmb_gee_value.setEditable(True)
        self.cmb_gee_value.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
        self.cmb_gee_value.lineEdit().setPlaceholderText("pick or type a value")

        self.cmb_gee_asset.currentIndexChanged.connect(self._on_gee_asset_changed)
        self.cmb_gee_field.currentIndexChanged.connect(self._on_gee_field_changed)

        gg.addRow("GEE asset:", self.cmb_gee_asset)
        gg.addRow("Field:", self.cmb_gee_field)
        gg.addRow("Value:", self.cmb_gee_value)
        self.stk_aoi.addWidget(w_gee)

        # --- Page 2: Canvas extent (no extra UI) ---
        w_canvas = QWidget()
        self.stk_aoi.addWidget(w_canvas)

        # --- Page 3: Drawn polygon ---
        w_drawn = QWidget()
        gd = QHBoxLayout(w_drawn)
        gd.setContentsMargins(0, 0, 0, 0)
        self.btn_draw_aoi = QPushButton("Draw polygon on canvas ...")
        self.btn_draw_aoi.clicked.connect(self._draw_aoi)
        self.lbl_custom = QLabel("no custom area")
        self.lbl_custom.setStyleSheet("color:#666;")
        gd.addWidget(self.btn_draw_aoi)
        gd.addWidget(self.lbl_custom, 1)
        self.stk_aoi.addWidget(w_drawn)

        g.addWidget(self.stk_aoi)

        self.custom_geom = None
        self.custom_name = ""

        v.addWidget(gb_source)

        # ===== OPTIONS (simplified) =====
        gb_opts = QGroupBox("Options")
        of = QFormLayout(gb_opts)

        # Dim opacity slider - always relevant
        from qgis.PyQt.QtWidgets import QSlider
        self.sld_dim = QSlider(Qt.Orientation.Horizontal)
        self.sld_dim.setRange(0, 100)
        self.sld_dim.setValue(55)
        self.lbl_dim = QLabel("55%")
        self.sld_dim.valueChanged.connect(
            lambda v: self.lbl_dim.setText("{}%".format(v)))
        h_dim = QHBoxLayout()
        h_dim.addWidget(self.sld_dim, 1)
        h_dim.addWidget(self.lbl_dim)
        hw_dim = QWidget()
        hw_dim.setLayout(h_dim)
        of.addRow("Dim / transparency:", hw_dim)

        self.chk_base = QCheckBox("Add Google Satellite basemap")
        self.chk_label = QCheckBox("Label the selected area (name)")
        self.chk_label.setChecked(True)

        of.addRow(self.chk_base)
        of.addRow(self.chk_label)

        self.ed_title = QLineEdit("")
        self.ed_title.setPlaceholderText("blank = use the area name")
        self.ed_institution = QLineEdit("")
        self.ed_institution.setPlaceholderText("YOUR INSTITUTION NAME")
        self.ed_sources = QLineEdit("")
        self.ed_sources.setPlaceholderText("optional")

        of.addRow("Map title:", self.ed_title)
        of.addRow("Institution:", self.ed_institution)

        self.cb_coord = QComboBox()
        self.cb_coord.addItems([
            "Auto (UTM for projected, DD for geographic)",
            "Decimal degrees (DD)",
            "Degrees-minutes-seconds (DMS)",
            "UTM (metres)",
        ])
        of.addRow("Grid coordinates:", self.cb_coord)
        of.addRow("Data sources:", self.ed_sources)

        v.addWidget(gb_opts)

        # ===== INSET MAPS =====
        v.addWidget(self._inset_group(
            "Inset 1", "KH_Provinces", "#ffe091",
            labels="highlight", zoom="whole",
            default_color2="#000000", fill_style2="BDiagonal"))
        v.addWidget(self._inset_group(
            "Inset 2", "Neighbours", "#ffb7f7",
            labels="all", zoom="around", default_value="Cambodia",
            default_color2="#000000", fill_style2="Diagonal X"))

        v.addStretch(1)

        scroll.setWidget(content)
        root.addWidget(scroll)

        bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        bb.addButton(help_button("layout", self), QDialogButtonBox.ButtonRole.HelpRole)
        bb.button(QDialogButtonBox.StandardButton.Ok).setText("Generate map")
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.buttonBox = bb
        root.addWidget(bb)

        self.resize(490, 650)
        self._on_source(0)

    # ---- GEE asset handlers ----
    def _on_gee_asset_changed(self, idx):
        """Populate field combo when GEE asset selection changes."""
        self.cmb_gee_field.clear()
        self.cmb_gee_field.addItem("(select field)", "")
        asset_path = self.cmb_gee_asset.currentData()
        if not asset_path:
            return
        try:
            from . import gee_utils as gu
            fields = gu.asset_field_names(asset_path)
            for fn in fields:
                self.cmb_gee_field.addItem(fn, fn)
            # Auto-select common name fields
            for pri in ("NAME_EN", "ADM1_NAME", "ADM2_NAME", "ADM3_NAME", "NAME"):
                if pri in fields:
                    self.cmb_gee_field.setCurrentIndex(
                        self.cmb_gee_field.findData(pri))
                    break
        except (AttributeError, TypeError):  # Qt/QGIS API compatibility
            pass

    def _on_gee_field_changed(self, idx):
        """Populate value combo when GEE field selection changes."""
        self.cmb_gee_value.clear()
        asset_path = self.cmb_gee_asset.currentData()
        field = self.cmb_gee_field.currentData()
        if not asset_path or not field:
            return
        try:
            from . import gee_utils as gu
            values = gu.asset_field_values(asset_path, field)
            self.cmb_gee_value.addItems(values)
        except (OSError, IOError):  # File operation
            pass

    # ---- area source ----
    def _on_source(self, idx):
        """Switch the stacked widget page so only the relevant AOI
        controls are visible.  QStackedWidget guarantees the non-active
        pages are completely removed from view (no ghost outlines,
        no disabled-style leftovers).
        """
        self.stk_aoi.setCurrentIndex(idx)

        if idx == 2:          # Current map canvas extent
            self._use_canvas_extent()
        elif idx != 3:        # Not drawn polygon
            self.lbl_custom.setText("no custom area")
            self.custom_geom = None
            self.custom_name = ""

    def _use_canvas_extent(self):
        from qgis.core import (QgsGeometry, QgsCoordinateTransform,
                               QgsCoordinateReferenceSystem)
        canvas = self.iface.mapCanvas()
        ext = canvas.extent()
        if ext.isEmpty():
            self.lbl_custom.setText("canvas has no extent")
            return
        g = QgsGeometry.fromRect(ext)
        crs = canvas.mapSettings().destinationCrs()
        wgs = QgsCoordinateReferenceSystem("EPSG:4326")
        if crs != wgs:
            try:
                ct = QgsCoordinateTransform(crs, wgs, QgsProject.instance())
                g.transform(ct)
            except (AttributeError, TypeError, RuntimeError):  # QGIS geometry
                pass
        self.custom_geom = g
        self.custom_name = self._name_for_custom("Canvas extent")
        self.lbl_custom.setText("using canvas extent ({})".format(
            self.custom_name))

    def _name_for_custom(self, fallback):
        try:
            from . import adminmap
            hint = adminmap.locate_unit(self.custom_geom.centroid().asPoint())
            if hint.get("name"):
                return hint["name"]
        except (AttributeError, TypeError, RuntimeError):  # QGIS geometry
            pass
        return fallback

    def _draw_aoi(self):
        from .dialogs.polygon_tool import PolygonDrawTool
        canvas = self.iface.mapCanvas()
        self._poly_tool = PolygonDrawTool(canvas)
        self._poly_tool.finished.connect(self._aoi_drawn)
        canvas.setMapTool(self._poly_tool)
        self.showMinimized()

    def _aoi_drawn(self, geom):
        from qgis.core import (QgsCoordinateTransform,
                               QgsCoordinateReferenceSystem)
        self.showNormal()
        self.raise_()
        if geom is None or geom.isEmpty():
            return
        crs = self.iface.mapCanvas().mapSettings().destinationCrs()
        wgs = QgsCoordinateReferenceSystem("EPSG:4326")
        if crs != wgs:
            try:
                ct = QgsCoordinateTransform(crs, wgs, QgsProject.instance())
                geom.transform(ct)
            except (AttributeError, TypeError, RuntimeError):  # QGIS geometry
                pass
        self.custom_geom = geom
        self.custom_name = self._name_for_custom("Drawn area")
        self.lbl_custom.setText("polygon set ({})".format(self.custom_name))

    def set_custom_geometry(self, geom_wgs84, name=""):
        if geom_wgs84 is None:
            return
        self.custom_geom = geom_wgs84
        self.custom_name = name or self._name_for_custom("AOI")
        self.cmb_source.setCurrentIndex(3)  # Drawn polygon
        self.lbl_custom.setText("AOI from Tool 1 ({})".format(
            self.custom_name))

    # ---- cascading ----
    def _on_prov(self):
        self.cmb_dist.blockSignals(True)
        self.cmb_dist.clear()

        # Get label based on data type
        if adminmap._has_cambodia:
            whole_label = "(whole province)"
        elif adminmap._has_global:
            whole_label = "(whole admin1)"
        else:
            whole_label = "(whole level1)"

        self.cmb_dist.addItem(whole_label, None)
        d = self.cmb_prov.currentData()
        if d:
            level_idx = 1 if len(adminmap.ADMIN_LEVELS) > 1 else 0
            for label, code, name in adminmap.read_units(adminmap.ADMIN_LEVELS[level_idx], d[0]):
                self.cmb_dist.addItem(label, (code, name))
        self.cmb_dist.blockSignals(False)
        self._on_dist()

    def _on_dist(self):
        if adminmap._has_cambodia:
            whole_label = "(whole district)"
        elif adminmap._has_global:
            whole_label = "(whole admin2)"
        else:
            whole_label = "(whole level2)"

        self.cmb_comm.clear()
        self.cmb_comm.addItem(whole_label, None)
        d = self.cmb_dist.currentData()
        if d:
            level_idx = 2 if len(adminmap.ADMIN_LEVELS) > 2 else 1
            for label, code, name in adminmap.read_units(adminmap.ADMIN_LEVELS[level_idx], d[0]):
                self.cmb_comm.addItem(label, (code, name))

    # ---- inset group ----
    def _polygon_layers(self):
        out = []
        for lyr in QgsProject.instance().mapLayers().values():
            if isinstance(lyr, QgsVectorLayer) and \
               lyr.geometryType() == QgsWkbTypes.GeometryType.PolygonGeometry:
                out.append(lyr)
        return out

    def _inset_group(self, title, prefer_bundled=None, default_color="#ffe091",
                     labels="none", zoom="whole", default_value="",
                     fill_style="Solid", default_color2="#000000",
                     fill_style2="Solid"):
        gb = QGroupBox(title)
        gb.setCheckable(True)
        gb.setChecked(False)
        f = QFormLayout(gb)

        cmb_layer = QComboBox()
        cmb_layer.addItem("(choose a polygon layer)", "")
        for name, path in bundled_boundaries():
            cmb_layer.addItem("[bundled] " + name, "bundled::" + path)
        for lyr in self._polygon_layers():
            cmb_layer.addItem(lyr.name(), lyr.id())
        if prefer_bundled:
            for i in range(cmb_layer.count()):
                d = cmb_layer.itemData(i) or ""
                if d.startswith("bundled::") and \
                   os.path.basename(d).lower().startswith(prefer_bundled.lower()):
                    cmb_layer.setCurrentIndex(i)
                    break

        cmb_field = QComboBox()
        cmb_field.addItem("(none — extent box)", "")
        cmb_value = QComboBox()
        cmb_value.setEditable(True)
        cmb_value.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
        cmb_value.lineEdit().setPlaceholderText("pick or type a value")

        if _HAVE_COLOR_BTN:
            w_color = QgsColorButton()
            try:
                w_color.setAllowOpacity(False)
                w_color.setColor(QColor(default_color))
            except (AttributeError, TypeError):  # Qt/QGIS API compatibility
                pass
        else:
            w_color = QLineEdit(default_color)

        chk_labels = QComboBox()
        chk_labels.addItems(["No labels", "Label all features",
                             "Label highlighted only"])
        chk_labels.setCurrentIndex({"none": 0, "all": 1, "highlight": 2}.get(labels, 0))

        cmb_zoom = QComboBox()
        cmb_zoom.addItems(["Whole layer", "Around highlighted feature"])
        cmb_zoom.setCurrentIndex(1 if zoom == "around" else 0)

        chk_clip = QCheckBox("Clip to 1st highlight (show only that unit)")
        chk_auto = QCheckBox("Highlight the selected area")

        def _load(spec):
            if not spec:
                return None
            if spec.startswith("bundled::"):
                return QgsVectorLayer(spec[9:], "tmp", "ogr")
            return QgsProject.instance().mapLayer(spec)

        def _fill_values():
            cur = cmb_value.currentText()
            cmb_value.clear()
            spec = cmb_layer.currentData()
            field = cmb_field.currentData()
            lyr = _load(spec) if (spec and field) else None
            if lyr is not None and lyr.isValid():
                idx = lyr.fields().indexOf(field)
                if idx >= 0:
                    try:
                        vals = sorted({str(x).strip() for x in lyr.uniqueValues(idx)
                                       if x not in (None, "")})
                    except (AttributeError, TypeError, RuntimeError):  # QGIS geometry
                        vals = []
                    cmb_value.addItems(vals)
            if cur:
                cmb_value.setCurrentText(cur)

        def _fill_fields():
            cmb_field.clear()
            cmb_field.addItem("(none — extent box)", "")
            lyr = _load(cmb_layer.currentData())
            names = [fl.name() for fl in lyr.fields()] if (lyr and lyr.isValid()) else []
            for nm in names:
                cmb_field.addItem(nm, nm)
            pick = None
            for pri in ("NAME_EN", "_EN", "COUNTRY", "NAME_1", "NAME_2", "NAME_3"):
                for i, nm in enumerate(names):
                    if pri in nm.upper():
                        pick = i + 1
                        break
                if pick:
                    break
            if pick is None:
                for i, nm in enumerate(names):
                    if any(k in nm.upper() for k in ("NAM", "NAME")):
                        pick = i + 1
                        break
            if pick is not None:
                cmb_field.setCurrentIndex(pick)

        cmb_field.currentIndexChanged.connect(_fill_values)
        cmb_layer.currentIndexChanged.connect(_fill_fields)
        _fill_fields()
        _fill_values()
        if default_value:
            cmb_value.setCurrentText(default_value)

        # Second highlight
        cmb_layer2 = QComboBox()
        cmb_layer2.addItem("(no second highlight)", "")
        for name2, path2 in bundled_boundaries():
            cmb_layer2.addItem("[bundled] " + name2, "bundled::" + path2)
        for lyr2 in self._polygon_layers():
            cmb_layer2.addItem(lyr2.name(), lyr2.id())

        cmb_field2 = QComboBox()
        cmb_field2.addItem("(field)", "")
        cmb_value2 = QComboBox()
        cmb_value2.setEditable(True)
        cmb_value2.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)

        if _HAVE_COLOR_BTN:
            w_color2 = QgsColorButton()
            try:
                w_color2.setAllowOpacity(False)
                w_color2.setColor(QColor(default_color2))
            except (AttributeError, TypeError):  # Qt/QGIS API compatibility
                pass
        else:
            w_color2 = QLineEdit(default_color2)

        def _fill_fields2():
            cmb_field2.clear()
            cmb_field2.addItem("(field)", "")
            lyr2 = _load(cmb_layer2.currentData())
            names2 = ([fl.name() for fl in lyr2.fields()]
                      if (lyr2 and lyr2.isValid()) else [])
            for nm in names2:
                cmb_field2.addItem(nm, nm)
            for i, nm in enumerate(names2):
                if "NAME_EN" in nm.upper() or "_EN" in nm.upper() or "NAME_" in nm.upper():
                    cmb_field2.setCurrentIndex(i + 1)
                    break

        def _fill_values2():
            cur = cmb_value2.currentText()
            cmb_value2.clear()
            spec2 = cmb_layer2.currentData()
            field2 = cmb_field2.currentData()
            lyr2 = _load(spec2) if (spec2 and field2) else None
            if lyr2 is not None and lyr2.isValid():
                idx2 = lyr2.fields().indexOf(field2)
                if idx2 >= 0:
                    try:
                        vals2 = sorted({str(x).strip()
                                        for x in lyr2.uniqueValues(idx2)
                                        if x not in (None, "")})
                    except (AttributeError, TypeError, RuntimeError):  # QGIS geometry
                        vals2 = []
                    cmb_value2.addItems(vals2)
            if cur:
                cmb_value2.setCurrentText(cur)

        cmb_layer2.currentIndexChanged.connect(_fill_fields2)
        cmb_field2.currentIndexChanged.connect(_fill_values2)

        f.addRow("Boundary layer:", cmb_layer)
        f.addRow("Highlight field:", cmb_field)
        f.addRow("Highlight value:", cmb_value)

        cmb_fill = QComboBox()
        cmb_fill.addItems(list(FILL_STYLES.keys()))
        cmb_fill.setCurrentText(fill_style or "Solid")
        h_col1 = QHBoxLayout()
        h_col1.addWidget(w_color, 1)
        h_col1.addWidget(QLabel("Fill:"))
        h_col1.addWidget(cmb_fill, 1)
        f.addRow("Highlight colour:", h_col1)

        chk_show2 = QCheckBox("Show 2nd highlight layer")
        chk_show2.setChecked(True)
        f.addRow("", chk_show2)
        f.addRow("2nd highlight layer:", cmb_layer2)
        f.addRow("2nd highlight field:", cmb_field2)
        f.addRow("2nd highlight value:", cmb_value2)

        def _toggle2(on):
            for wdg in (cmb_layer2, cmb_field2, cmb_value2, cmb_fill2, w_color2):
                wdg.setEnabled(bool(on))
        chk_show2.toggled.connect(_toggle2)

        cmb_fill2 = QComboBox()
        cmb_fill2.addItems(list(FILL_STYLES.keys()))
        cmb_fill2.setCurrentText(fill_style2 or "Solid")
        h_col2 = QHBoxLayout()
        h_col2.addWidget(w_color2, 1)
        h_col2.addWidget(QLabel("Fill:"))
        h_col2.addWidget(cmb_fill2, 1)
        f.addRow("2nd highlight colour:", h_col2)
        f.addRow("Zoom to:", cmb_zoom)
        f.addRow("", chk_clip)
        f.addRow("Labels:", chk_labels)
        f.addRow("", chk_auto)

        self.insets.append({
            "group": gb, "layer": cmb_layer, "field": cmb_field,
            "value": cmb_value, "color": w_color,
            "fill": cmb_fill, "fill2": cmb_fill2,
            "show2": chk_show2,
            "layer2": cmb_layer2, "field2": cmb_field2,
            "value2": cmb_value2, "color2": w_color2,
            "labels": chk_labels, "zoom": cmb_zoom,
            "clip": chk_clip,
            "auto": chk_auto
        })
        return gb

    # ---- programmatic configuration ----
    def configure_inset(self, index, layer_name=None, field=None, value=None,
                        color=None, layer2_name=None, field2=None,
                        value2=None, color2=None, labels=None, zoom=None,
                        clip_focus=None, enabled=True,
                        fill_style=None, fill_style2=None,
                        clip_to_parent=None, show2=None):
        if index >= len(self.insets):
            return
        w = self.insets[index]
        w["group"].setChecked(bool(enabled))

        def _pick_layer(cmb, want):
            for i in range(cmb.count()):
                d = cmb.itemData(i) or ""
                if isinstance(d, str) and d.startswith("bundled::") and \
                        os.path.basename(d).lower().startswith(want.lower()):
                    cmb.setCurrentIndex(i)
                    return True
            return False

        def _pick_field(cmb, want):
            for i in range(cmb.count()):
                if str(cmb.itemData(i) or "").upper() == want.upper():
                    cmb.setCurrentIndex(i)
                    return True
            return False

        from qgis.PyQt.QtWidgets import QApplication
        if layer_name:
            _pick_layer(w["layer"], layer_name)
            QApplication.processEvents()
        if field:
            _pick_field(w["field"], field)
            QApplication.processEvents()
        if value is not None:
            w["value"].setCurrentText(value)
        if color is not None:
            try:
                w["color"].setColor(QColor(color))
            except (AttributeError, TypeError):  # Qt/QGIS API compatibility
                try:
                    w["color"].setText(color)
                except (AttributeError, TypeError):  # Qt/QGIS API compatibility
                    pass
        if layer2_name:
            _pick_layer(w["layer2"], layer2_name)
            QApplication.processEvents()
        if field2:
            _pick_field(w["field2"], field2)
            QApplication.processEvents()
        if value2 is not None:
            w["value2"].setCurrentText(value2)
        if color2 is not None:
            try:
                w["color2"].setColor(QColor(color2))
            except (AttributeError, TypeError):  # Qt/QGIS API compatibility
                try:
                    w["color2"].setText(color2)
                except (AttributeError, TypeError):  # Qt/QGIS API compatibility
                    pass
        if fill_style is not None and "fill" in w:
            w["fill"].setCurrentText(fill_style)
        if fill_style2 is not None and "fill2" in w:
            w["fill2"].setCurrentText(fill_style2)
        if labels is not None:
            w["labels"].setCurrentIndex(
                {"none": 0, "all": 1, "highlight": 2}.get(labels, 0))
        if zoom is not None:
            w["zoom"].setCurrentIndex(1 if zoom == "around" else 0)
        if clip_focus is not None:
            if "clip" in w:
                w["clip"].setChecked(bool(clip_focus))
            else:
                w["clip_focus"] = bool(clip_focus)
        if clip_to_parent is not None:
            w["clip_to_parent"] = clip_to_parent
        if show2 is not None and "show2" in w:
            w["show2"].setChecked(bool(show2))
        w["auto"].setChecked(False)

    # ---- output ----
    def params(self):
        prov = self.cmb_prov.currentData()
        dist = self.cmb_dist.currentData()
        comm = self.cmb_comm.currentData()

        if comm:
            level, code, name = adminmap.ADMIN_LEVELS[2], comm[0], comm[1]
        elif dist:
            level, code, name = adminmap.ADMIN_LEVELS[1], dist[0], dist[1]
        elif prov:
            level, code, name = adminmap.ADMIN_LEVELS[0], prov[0], prov[1]
        else:
            level, code, name = None, None, None

        # Always include context layers - no "Layers to include" filtering
        layers = list(adminmap.CONTEXT.keys())

        insets = []
        for w in self.insets:
            cw = w["color"]
            try:
                hexc = cw.color().name()
            except (AttributeError, TypeError, RuntimeError):  # Safe fallback
                hexc = cw.text().strip()
            try:
                hexc2 = w["color2"].color().name()
            except (AttributeError, TypeError, RuntimeError):  # Safe fallback
                hexc2 = w["color2"].text().strip()
            insets.append({
                "enabled": w["group"].isChecked(),
                "layer_id": w["layer"].currentData() or "",
                "field": w["field"].currentData() or "",
                "value": w["value"].currentText().strip(),
                "color": hexc or "#ffe091",
                "fill_style": w["fill"].currentText(),
                "fill_style2": w["fill2"].currentText(),
                "layer2_id": (w["layer2"].currentData() or ""
                              if w.get("show2") is None
                              or w["show2"].isChecked() else ""),
                "field2": w["field2"].currentData() or "",
                "value2": (w["value2"].currentText().strip()
                           if w.get("show2") is None
                           or w["show2"].isChecked() else ""),
                "color2": hexc2 or "#000000",
                "labels": {0: "none", 1: "all", 2: "highlight"}.get(
                    w["labels"].currentIndex(), "none"),
                "zoom": ("around" if w["zoom"].currentIndex() == 1 else "whole"),
                "auto_unit": w["auto"].isChecked(),
                "clip_focus": (w["clip"].isChecked()
                               if "clip" in w else
                               bool(w.get("clip_focus", False))),
                "clip_to_parent": w.get("clip_to_parent"),
            })

        src = self.cmb_source.currentIndex()
        custom = self.custom_geom if src in (2, 3) else None  # indices shifted
        if custom is not None:
            level = level or "custom"
            name = self.custom_name or name or "AOI"

        return {
            "custom_geom": custom,
            "level": level, "unit_code": code, "unit_name": name,
            "prov_name": prov[1] if prov else "",
            "layers": layers,
            "focus": "surround",  # Always use surround mode
            "dim_opacity": self.sld_dim.value() / 100.0,
            "basemap": self.chk_base.isChecked(),
            "label_unit": self.chk_label.isChecked(),
            "label_all": True,  # Auto-enable for surroundings
            "title": self.ed_title.text().strip(),
            "institution": (self.ed_institution.text().strip()
                            or "YOUR INSTITUTION NAME"),
            "coord_format": ["auto", "dd", "dms", "utm"][
                self.cb_coord.currentIndex()],
            "data_sources": self.ed_sources.text().strip(),
            "insets": insets,
        }
