# SPDX-License-Identifier: GPL-3.0-or-later
"""VesselAPI REST polling reader.

A QObject that polls VesselAPI's bounding-box endpoint on a timer using Qt's
QNetworkAccessManager (bundled with QGIS — no new dependency, no extra thread) and
emits ``recordReceived`` per vessel. Presents the same lifecycle as the other
readers (start/stop/isRunning + connected/disconnected/errorOccurred/finished) so
the source dialog treats it uniformly.

The box is own-position +/- a radius; :meth:`set_bbox` updates it for the next
poll. Auth is a Bearer token header.
"""
import json

from qgis.PyQt.QtCore import QObject, pyqtSignal, QTimer, QUrl, QUrlQuery
from qgis.PyQt.QtNetwork import (QNetworkAccessManager, QNetworkRequest,
                                 QNetworkReply)

from .internet_sources import vesselapi_params, vesselapi_to_records


class VesselApiReader(QObject):
    lineReceived = pyqtSignal(str)          # interface parity (unused)
    recordReceived = pyqtSignal(object)
    errorOccurred = pyqtSignal(str)
    connected = pyqtSignal()
    disconnected = pyqtSignal()
    finished = pyqtSignal()

    def __init__(self, url, api_key, bbox, poll_s=30, parent=None):
        super().__init__(parent)
        self._url = url
        self._key = api_key or ""
        self._bbox = bbox
        self._poll_ms = max(5, int(poll_s)) * 1000
        self._nam = QNetworkAccessManager(self)
        self._nam.finished.connect(self._on_reply)
        self._timer = QTimer(self)
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._poll)
        self._running = False
        self._want = False

    # ---- lifecycle (reader interface) ----------------------------------- #
    def start(self):
        self._want = True
        self._running = True
        self.connected.emit()           # REST has no persistent socket: "live" now
        self._poll()

    def stop(self):
        self._want = False
        self._running = False
        self._timer.stop()
        QTimer.singleShot(0, self.finished.emit)

    def isRunning(self):
        return self._running

    def set_bbox(self, bbox):
        self._bbox = bbox               # applied on the next poll

    # ---- internals ------------------------------------------------------ #
    def _poll(self):
        if not self._want:
            return
        url = QUrl(self._url)
        q = QUrlQuery()
        for k, v in vesselapi_params(self._bbox).items():
            q.addQueryItem(k, str(v))
        url.setQuery(q)
        req = QNetworkRequest(url)
        req.setRawHeader(b"Authorization",
                         b"Bearer " + self._key.encode("ascii", "ignore"))
        req.setRawHeader(b"Accept", b"application/json")
        self._nam.get(req)

    def _on_reply(self, reply):
        try:
            if int(reply.error()) != int(QNetworkReply.NoError):
                self.errorOccurred.emit(reply.errorString())
            else:
                raw = bytes(reply.readAll()).decode("utf-8", "ignore")
                for rec in vesselapi_to_records(json.loads(raw)):
                    self.recordReceived.emit(rec)
        except Exception as exc:                    # noqa: BLE001
            self.errorOccurred.emit(str(exc))
        finally:
            try:
                reply.deleteLater()
            except Exception:                       # noqa: BLE001  # nosec B110
                pass
            if self._want:
                self._timer.start(self._poll_ms)
