# -*- coding: utf-8 -*-
"""
LVT Layer Compare Dialog — dialog.py
======================================
Author  : Lộc Vũ Trung / LVT Center
License : MIT

Panel nổi (QDockWidget) điều khiển so sánh 2 lớp bản đồ.

FIX v3:
  - Full i18n: mọi UI string đều qua tr()
  - _retranslate_ui(): cập nhật ngay khi đổi ngôn ngữ (không cần restart)
  - language_signals.language_changed → _retranslate_ui kết nối tự động
  - Closure NameError fix (default-arg capture j=job)
"""

from qgis.PyQt.QtCore import Qt, pyqtSlot
from qgis.PyQt.QtGui import QIcon, QColor
from qgis.PyQt.QtWidgets import (
    QDockWidget, QWidget, QVBoxLayout, QHBoxLayout,
    QLabel, QComboBox, QPushButton, QFrame,
    QButtonGroup, QAbstractButton, QSizePolicy,
    QMessageBox
)
from qgis.core import QgsProject, QgsMapLayer
from qgis.gui import QgisInterface

from ..shared.qt_compat import QtCompat, LayerType
from ..shared.i18n import tr, language_signals

from .tool import CompareCanvasItem, LayerCompareTool


# ── Helpers ────────────────────────────────────────────────────────────────
def _sep() -> QFrame:
    f = QFrame()
    f.setFrameShape(QFrame.Shape.HLine)
    f.setFrameShadow(QFrame.Shadow.Sunken)
    f.setStyleSheet("color: #ddd; margin: 1px 0;")
    return f


def _lbl(text: str = "", bold: bool = False) -> QLabel:
    lb = QLabel(text)
    lb.setStyleSheet(
        "font-weight: bold; color: #222; font-size: 11px;" if bold
        else "color: #555; font-size: 10px;"
    )
    return lb


def _btn(text: str, color: str = "#333", bg: str = "#ececec",
         checkable: bool = False) -> QPushButton:
    b = QPushButton(text)
    b.setCheckable(checkable)
    b.setFixedHeight(26)
    b.setStyleSheet(f"""
        QPushButton {{
            background: {bg}; color: {color};
            border: 1px solid #bbb; border-radius: 4px;
            padding: 2px 8px; font-size: 11px;
        }}
        QPushButton:hover   {{ background: #d0e8ff; border-color: #3a8dde; }}
        QPushButton:checked {{ background: #3a8dde; color: white; border-color: #2070bb; }}
        QPushButton:pressed {{ background: #2070bb; color: white; }}
        QPushButton:disabled{{ background: #f5f5f5; color: #bbb; }}
    """)
    return b


# ── Main dialog ────────────────────────────────────────────────────────────
class LayerCompareDialog(QDockWidget):
    """
    Panel điều khiển so sánh 2 lớp bản đồ.

    v3 improvements:
      - Full i18n: mọi text đều qua tr()
      - _retranslate_ui(): cập nhật ngay khi đổi ngôn ngữ
      - language_signals kết nối tự động, disconnect khi đóng
    """

    # English title — tr() will translate when VI selected
    TITLE_KEY = "Layer Compare"

    def __init__(self, iface: QgisInterface):
        super().__init__(tr(self.TITLE_KEY), iface.mainWindow())
        self.iface   = iface
        self.canvas  = iface.mapCanvas()
        self._active = False
        self._prev_tool = None
        self._layer_b_was_visible = False

        self._item = CompareCanvasItem(self.canvas)
        self._tool = LayerCompareTool(self.canvas, self._item)

        self._build_ui()
        self._connect_signals()

        self.setAllowedAreas(
            QtCompat.LeftDockWidgetArea | QtCompat.RightDockWidgetArea
            | QtCompat.TopDockWidgetArea | QtCompat.BottomDockWidgetArea
        )
        self.setFeatures(
            QDockWidget.DockWidgetFeature.DockWidgetMovable
            | QDockWidget.DockWidgetFeature.DockWidgetFloatable
            | QDockWidget.DockWidgetFeature.DockWidgetClosable
        )
        self.setFloating(True)
        self.setMinimumWidth(240)

    # ── UI ────────────────────────────────────────────────────────────────
    def _build_ui(self):
        root = QWidget()
        root.setStyleSheet("background: #f8f8f8;")
        main = QVBoxLayout(root)
        main.setContentsMargins(8, 6, 8, 8)
        main.setSpacing(4)

        # ── Layer A ───────────────────────────────────────────
        self.lbl_layer_a = _lbl(bold=True)
        main.addWidget(self.lbl_layer_a)
        self.cb_layer_a = QComboBox()
        self.cb_layer_a.setFixedHeight(22)
        self.cb_layer_a.setStyleSheet("font-size: 11px;")
        main.addWidget(self.cb_layer_a)

        # ── Layer B ───────────────────────────────────────────
        self.lbl_layer_b = _lbl(bold=True)
        main.addWidget(self.lbl_layer_b)
        self.cb_layer_b = QComboBox()
        self.cb_layer_b.setFixedHeight(22)
        self.cb_layer_b.setStyleSheet("font-size: 11px;")
        main.addWidget(self.cb_layer_b)

        # ── Refresh ───────────────────────────────────────────
        self.btn_refresh = _btn("")
        main.addWidget(self.btn_refresh)

        main.addWidget(_sep())

        # ── Direction row ─────────────────────────────────────
        dir_row = QHBoxLayout()
        dir_row.setSpacing(4)
        self.lbl_dir = _lbl(bold=True)
        main.addWidget(self.lbl_dir)
        self.btn_horiz = _btn("", checkable=True)
        self.btn_vert  = _btn("", checkable=True)
        self.btn_horiz.setChecked(True)
        self._dir_group = QButtonGroup()
        self._dir_group.addButton(self.btn_horiz, 0)
        self._dir_group.addButton(self.btn_vert,  1)
        self._dir_group.setExclusive(True)
        dir_row.addWidget(self.btn_horiz)
        dir_row.addWidget(self.btn_vert)
        main.addLayout(dir_row)

        # ── Swap + Center in one row ──────────────────────────
        sw_row = QHBoxLayout()
        sw_row.setSpacing(4)
        self.btn_swap   = _btn("")
        self.btn_center = _btn("")
        sw_row.addWidget(self.btn_swap)
        sw_row.addWidget(self.btn_center)
        main.addLayout(sw_row)

        main.addWidget(_sep())

        # ── Status ────────────────────────────────────────────
        self.lbl_status = QLabel()
        self.lbl_status.setAlignment(QtCompat.AlignCenter)
        self.lbl_status.setStyleSheet(
            "color: #999; font-size: 10px; padding: 2px;"
        )
        self.lbl_status.setWordWrap(True)
        main.addWidget(self.lbl_status)

        # ── Activate / Stop ───────────────────────────────────
        act_row = QHBoxLayout()
        act_row.setSpacing(4)
        self.btn_activate = _btn("", color="white", bg="#2e7d32")
        self.btn_stop     = _btn("", color="white", bg="#c62828")
        self.btn_stop.setEnabled(False)
        act_row.addWidget(self.btn_activate)
        act_row.addWidget(self.btn_stop)
        main.addLayout(act_row)

        self.setWidget(root)

        # Apply translations for first render
        self._retranslate_ui()

    # ── Retranslation ─────────────────────────────────────────────────────
    @pyqtSlot()
    def _retranslate_ui(self, _lang=None):
        """Update all UI text to current language. Called on language change."""
        self.setWindowTitle(f"LVT — {tr(self.TITLE_KEY)}")
        self.lbl_layer_a.setText(tr("Layer A  (base / right / below)"))
        self.lbl_layer_b.setText(tr("Layer B  (overlay / left / above)"))
        self.btn_refresh.setText(tr("🔄 Refresh Layer List"))
        self.lbl_dir.setText(tr("Direction:"))
        self.btn_horiz.setText(tr("↔ Horizontal"))
        self.btn_vert.setText(tr("↕ Vertical"))
        self.btn_swap.setText(tr("🔃 Swap B"))
        self.btn_center.setText(tr("⊕ Center"))
        self.btn_swap.setToolTip(tr("Swap B side display to opposite"))
        self.btn_center.setToolTip(tr("Set divider to center (50%)"))
        self.btn_activate.setText(tr("▶ Activate"))
        self.btn_stop.setText(tr("⏹ Stop"))

        # Only update status if in idle state (don't overwrite active status)
        if not self._active:
            self.lbl_status.setText(tr("⏸ Inactive"))

    # ── Signals ───────────────────────────────────────────────────────────
    def _connect_signals(self):
        self.btn_refresh.clicked.connect(self._populate_layers)
        self.btn_activate.clicked.connect(self._activate)
        self.btn_stop.clicked.connect(self._stop)
        self.btn_swap.clicked.connect(self._swap)
        self.btn_center.clicked.connect(self._reset_center)
        self._dir_group.buttonClicked.connect(self._direction_changed)

        self.canvas.extentsChanged.connect(self._item.refresh)

        self._item.signals.rendering_started.connect(self._on_render_start)
        self._item.signals.rendering_done.connect(self._on_render_done)

        QgsProject.instance().layersAdded.connect(lambda _: self._populate_layers())
        QgsProject.instance().layersRemoved.connect(lambda _: self._populate_layers())

        self._tool.deactivated.connect(self._on_tool_deactivated)

        # Live language switching
        language_signals.language_changed.connect(self._retranslate_ui)

    # ── Layer list ────────────────────────────────────────────────────────
    def _populate_layers(self):
        layers = [
            lyr for lyr in QgsProject.instance().mapLayers().values()
            if lyr.isSpatial()
        ]
        layers.sort(key=lambda lyr: lyr.name().lower())

        sel_a = self.cb_layer_a.currentData()
        sel_b = self.cb_layer_b.currentData()

        self.cb_layer_a.blockSignals(True)
        self.cb_layer_b.blockSignals(True)
        self.cb_layer_a.clear()
        self.cb_layer_b.clear()

        for lyr in layers:
            icon_txt = "R" if lyr.type() == LayerType.Raster else "V"
            self.cb_layer_a.addItem(f"[{icon_txt}] {lyr.name()}", lyr.id())
            self.cb_layer_b.addItem(f"[{icon_txt}] {lyr.name()}", lyr.id())

        if sel_a:
            idx = self.cb_layer_a.findData(sel_a)
            if idx >= 0:
                self.cb_layer_a.setCurrentIndex(idx)
        if sel_b:
            idx = self.cb_layer_b.findData(sel_b)
            if idx >= 0:
                self.cb_layer_b.setCurrentIndex(idx)

        self.cb_layer_a.blockSignals(False)
        self.cb_layer_b.blockSignals(False)

    def _get_layer(self, combo: QComboBox):
        lid = combo.currentData()
        return QgsProject.instance().mapLayer(lid) if lid else None

    # ── Activate / Stop ───────────────────────────────────────────────────
    @pyqtSlot()
    def _activate(self):
        layer_a = self._get_layer(self.cb_layer_a)
        layer_b = self._get_layer(self.cb_layer_b)

        if not layer_a or not layer_b:
            QMessageBox.warning(
                self,
                tr("Layer Compare"),
                tr("Please select both Layer A and Layer B.")
            )
            return

        if layer_a.id() == layer_b.id():
            QMessageBox.warning(
                self,
                tr("Layer Compare"),
                tr("Layer A and Layer B must be different.")
            )
            return

        root = QgsProject.instance().layerTreeRoot()

        # Ensure Layer A is visible (background)
        node_a = root.findLayer(layer_a.id())
        if node_a:
            node_a.setItemVisibilityChecked(True)

        # Hide Layer B from layer tree: CompareCanvasItem renders it in the right position
        node_b = root.findLayer(layer_b.id())
        if node_b:
            self._layer_b_was_visible = node_b.isVisible()
            node_b.setItemVisibilityChecked(False)
        else:
            self._layer_b_was_visible = False

        # Start overlay item
        self._item.horizontal = self.btn_horiz.isChecked()
        self._item.divider_pos = 0.5
        self._item.swap = False
        self._item.set_layer(layer_b)
        self._item.activate()

        # Activate tool (save old tool for restoration)
        self._prev_tool = self.canvas.mapTool()
        self.canvas.setMapTool(self._tool)

        self._active = True
        self.btn_activate.setEnabled(False)
        self.btn_stop.setEnabled(True)

        self.lbl_status.setText(
            f"🟢 {layer_a.name()}  ↔  {layer_b.name()}"
        )
        self.lbl_status.setStyleSheet(
            "color: #1a6b1a; font-weight: bold; font-size: 10px; padding: 2px;"
        )

    @pyqtSlot()
    def _stop(self):
        """Stop completely — called by Stop button or closeEvent."""
        # IMPORTANT: set _active=False BEFORE changing tool
        # Prevents _on_tool_deactivated calling _stop() again
        self._active = False

        self._item.deactivate()

        # Restore Layer B visibility if it was visible before
        self._restore_layer_b_visibility()

        # Restore old tool
        if self._prev_tool:
            try:
                self.canvas.setMapTool(self._prev_tool)
            except Exception:
                self.canvas.unsetMapTool(self._tool)
            self._prev_tool = None
        else:
            self.canvas.unsetMapTool(self._tool)

        self.btn_activate.setEnabled(True)
        self.btn_stop.setEnabled(False)

        self.lbl_status.setText(tr("⏸ Stopped"))
        self.lbl_status.setStyleSheet(
            "color: #999; font-size: 10px; padding: 2px;"
        )

    def _restore_layer_b_visibility(self):
        """Restore Layer B in layer tree if it was visible before."""
        if not self._layer_b_was_visible:
            return
        layer_b = self._get_layer(self.cb_layer_b)
        if not layer_b:
            return
        root = QgsProject.instance().layerTreeRoot()
        node_b = root.findLayer(layer_b.id())
        if node_b:
            node_b.setItemVisibilityChecked(True)

    @pyqtSlot()
    def _on_tool_deactivated(self):
        """
        Called when user switches to another tool (zoom, pan QGIS toolbar...).

        FIX: Do NOT call _stop() — only update UI so user knows tool changed.
        Overlay item keeps running; user just needs to click Activate again.
        If already stopped, ignore.
        """
        if not self._active:
            return
        # Don't stop completely — only allow re-activation
        self._active = False
        self.btn_activate.setEnabled(True)
        self.btn_stop.setEnabled(True)   # still allow full stop
        self.lbl_status.setText(tr("⚠️ Tool changed — click Activate again"))
        self.lbl_status.setStyleSheet(
            "color: #b26000; font-size: 10px; padding: 2px;"
        )
        # Don't deactivate item — overlay still shows, zoom/pan still work

    # ── Controls ──────────────────────────────────────────────────────────
    @pyqtSlot(QAbstractButton)
    def _direction_changed(self, btn: QAbstractButton):
        self._item.horizontal = (btn is self.btn_horiz)
        self._item.updateCanvas()

    @pyqtSlot()
    def _swap(self):
        self._item.swap = not self._item.swap
        self._item.updateCanvas()

    @pyqtSlot()
    def _reset_center(self):
        self._item.divider_pos = 0.5
        self._item.updateCanvas()

    # ── Render feedback ───────────────────────────────────────────────────
    @pyqtSlot(str)
    def _on_render_start(self, name: str):
        # Use format string substitution from translations
        tpl = tr("⏳ Rendering: {name}…")
        self.lbl_status.setText(tpl.replace("{name}", name))
        self.lbl_status.setStyleSheet(
            "color: #b26000; font-size: 10px; padding: 2px;"
        )

    @pyqtSlot()
    def _on_render_done(self):
        if self._active:
            layer_a = self._get_layer(self.cb_layer_a)
            layer_b = self._get_layer(self.cb_layer_b)
            if layer_a and layer_b:
                self.lbl_status.setText(
                    f"🟢 {layer_a.name()}  ↔  {layer_b.name()}"
                )
                self.lbl_status.setStyleSheet(
                    "color: #1a6b1a; font-weight: bold; font-size: 10px; padding: 2px;"
                )

    # ── Lifecycle ─────────────────────────────────────────────────────────
    def showEvent(self, event):
        super().showEvent(event)
        self._populate_layers()

    def closeEvent(self, event):
        if self._active:
            self._stop()
        elif self._item._active:
            # Item still active even if tool was changed
            self._item.deactivate()
            self._restore_layer_b_visibility()

        # Disconnect language signal to avoid dangling references
        try:
            language_signals.language_changed.disconnect(self._retranslate_ui)
        except Exception:  # nosec B110
            pass  # nosec B110

        try:
            self.canvas.extentsChanged.disconnect(self._item.refresh)
        except Exception:  # nosec B110
            pass  # nosec B110

        super().closeEvent(event)
