"""Routes tab — a clean, sortable table of the fairways/routes that currently
have vessels in coverage, with per-route Inbound / Outbound / Idle counts.

Rows are sorted by fairway name (the model does the sorting) so the order stays
stable as vessel counts change. Clicking a row opens that route's leg in the route
manager. Reads everything live from the StreamWidget (matcher + route model);
holds no state of its own beyond the table.
"""
from qgis.PyQt.QtWidgets import (
    QWidget, QVBoxLayout, QLabel, QTableWidget, QTableWidgetItem,
    QHeaderView, QAbstractItemView)
from qgis.PyQt.QtCore import QTimer, Qt
from qgis.PyQt.QtGui import QColor

_CUSTOM_BLUE = QColor(0, 90, 200)   # custom (user-drawn) routes are shown in blue


class RoutesTab(QWidget):
    """Live per-route vessel table; a row click opens the route's leg. The
    Bind-width column is editable per route and persists via the stream."""

    _WIDTH_COL = 4

    def __init__(self, stream, parent=None):
        """Build the table over a StreamWidget (its matcher/model supply rows;
        its open_route_leg opens the manager). Refreshes on a 2 s timer."""
        super().__init__(parent)
        self._stream = stream
        lay = QVBoxLayout(self)
        hint = QLabel("Fairways with vessels in coverage — click a row to open "
                      "its leg. 'Bind width' is how far off the centreline a "
                      "vessel still binds (m); widen it for broad tidal fairways.")
        hint.setStyleSheet("color:#666;")
        hint.setWordWrap(True)
        lay.addWidget(hint)

        self.table = QTableWidget(0, 5)
        self.table.setHorizontalHeaderLabels(
            ["Fairway", "Inbound", "Outbound", "Idle", "Bind width (m)"])
        self.table.verticalHeader().setVisible(False)
        # editing allowed only for the Bind-width cells (per-item flags below)
        self.table.setEditTriggers(
            QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setSelectionMode(QAbstractItemView.SingleSelection)
        hh = self.table.horizontalHeader()
        hh.setSectionResizeMode(0, QHeaderView.Stretch)
        for c in (1, 2, 3, self._WIDTH_COL):
            hh.setSectionResizeMode(c, QHeaderView.ResizeToContents)
        self.table.cellClicked.connect(self._on_cell)
        self.table.itemChanged.connect(self._on_item_changed)
        lay.addWidget(self.table)

        self._timer = QTimer(self)
        self._timer.setInterval(2000)
        self._timer.timeout.connect(self.refresh)
        self._timer.start()
        self.refresh()

    def refresh(self):
        """Rebuild the table from the live per-route rows (already sorted by the
        model: by fairway name, for a stable order). Preserves the selected route across
        rebuilds where possible. Skips while the user is editing a cell, and
        blocks itemChanged during the rebuild so programmatic fills don't look
        like edits."""
        if self.table.state() == QAbstractItemView.EditingState:
            return                              # don't clobber an in-progress edit
        try:
            rows = self._stream.route_rows()
        except Exception:                      # noqa: BLE001
            rows = []
        sel = self._selected_routeid()
        self.table.blockSignals(True)
        try:
            self.table.setRowCount(len(rows))
            sel_row = -1
            noedit = Qt.ItemIsSelectable | Qt.ItemIsEnabled
            for i, r in enumerate(rows):
                rid = r.get("routeid")
                custom = isinstance(rid, int) and rid < 0   # user-drawn route
                label = str(r.get("name", ""))
                if custom:
                    label += "  (custom)"
                name = QTableWidgetItem(label)
                name.setData(Qt.UserRole, rid)
                name.setFlags(noedit)
                self.table.setItem(i, 0, name)
                for col, key in ((1, "inbound"), (2, "outbound"), (3, "idle")):
                    it = QTableWidgetItem(str(r.get(key, 0)))
                    it.setTextAlignment(Qt.AlignCenter)
                    it.setFlags(noedit)
                    self.table.setItem(i, col, it)
                tol = self._stream.route_tolerance(rid)
                wit = QTableWidgetItem("%d" % round(tol))
                wit.setTextAlignment(Qt.AlignCenter)
                wit.setData(Qt.UserRole, rid)
                wit.setFlags(noedit | Qt.ItemIsEditable)
                self.table.setItem(i, self._WIDTH_COL, wit)
                if custom:                          # blue = custom route
                    for col in range(self.table.columnCount()):
                        cell = self.table.item(i, col)
                        if cell is not None:
                            cell.setForeground(_CUSTOM_BLUE)
                if rid == sel:
                    sel_row = i
            if sel_row >= 0:
                self.table.selectRow(sel_row)
        finally:
            self.table.blockSignals(False)

    def _on_item_changed(self, item):
        """A Bind-width cell was edited: persist the new tolerance via the stream
        and normalise the displayed value."""
        if item.column() != self._WIDTH_COL:
            return
        rid = item.data(Qt.UserRole)
        if rid is None:
            return
        self._stream.set_route_tolerance(rid, item.text())
        self.table.blockSignals(True)
        try:
            item.setText("%d" % round(self._stream.route_tolerance(rid)))
        finally:
            self.table.blockSignals(False)

    def _selected_routeid(self):
        """routeid of the currently selected row, or None."""
        items = self.table.selectedItems()
        if not items:
            return None
        it = self.table.item(items[0].row(), 0)
        return it.data(Qt.UserRole) if it is not None else None

    def _on_cell(self, row, col):
        """Open the clicked route's leg — except on the Bind-width column, where a
        click is for editing the value, not opening the leg."""
        if col == self._WIDTH_COL:
            return
        it = self.table.item(row, 0)
        if it is None:
            return
        rid = it.data(Qt.UserRole)
        if rid is not None:
            self._stream.open_route_leg(rid)

    def shutdown(self):
        """Stop the refresh timer (called by the dock on unload)."""
        try:
            self._timer.stop()
        except Exception:  # nosec B110
            pass
