"""pyqtgraph PlotWidget wrapper for embedding in QML.

Exposes a QWidget with a pyqtgraph PlotWidget that can be displayed
via QML's `import QtWidgets` or `QtWidgets.QWidget` integration.
"""

from __future__ import annotations

import logging
from typing import Any, Optional

import pyqtgraph as pg
from pyqtgraph import PlotWidget
from PySide6.QtCore import Property, QObject, Qt, Signal, Slot
from PySide6.QtWidgets import QWidget

log = logging.getLogger(__name__)

class GraphView(QObject):
    """QML-exposed controller for a pyqtgraph line chart.

    Accepts sensor temperature data (list of lists) and renders
    each sensor as a separate line series.
    """

    # Set lazily in updateTrend (guarded by hasattr); declared for mypy.
    _trend_line: Any

    # ---- signals ----
    dataReady = Signal(object)  # {"success": bool, "sensory_names": list, "series": list}

    def __init__(self, parent: Optional[QObject] = None) -> None:
        super().__init__(parent)
        self._plot_widget: Optional[PlotWidget] = None
        self._plot_window: Optional[QWidget] = None
        self._series: list[Any] = []
        self._sensor_names: list[str] = []

    @Property(object, notify=dataReady)
    def plotWidget(self) -> Any:
        """Return the QWidget hosting the pyqtgraph PlotWidget for QML embedding."""
        return self._plot_window

    @Slot()
    def createPlotWidget(self, parent_widget: Optional[QWidget] = None) -> None:
        """Create and return a QWidget containing a pyqtgraph PlotWidget.

        Args:
            parent_widget: Optional parent widget.
        """
        pg.setConfigOption("background", "default")
        pg.setConfigOption("foreground", "default")

        self._plot_window = QWidget(parent=parent_widget)
        self._plot_widget = PlotWidget()
        self._plot_widget.setBackground("default")

        from PySide6.QtWidgets import QVBoxLayout
        layout = QVBoxLayout(self._plot_window)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self._plot_widget)

        # Set axis labels
        self._plot_widget.setLabel("left", "Temperature", units="°C")
        self._plot_widget.setLabel("bottom", "Measurement Interval")
        self._plot_widget.setTitle("Sensor Temperature Over Time")

    @Slot(str, str)
    def updateChart(self, sensor_names_str: str, series_data_str: str) -> None:
        """Update the chart with sensor data.

        Args:
            sensor_names_str: Comma-separated sensor names (e.g. "Sensor1,Sensor2").
            series_data_str: JSON-like string of nested lists for each sensor's values.
        """
        import json

        if not self._plot_widget:
            log.warning("PlotWidget not created yet")
            return

        try:
            sensor_names = [s.strip() for s in sensor_names_str.split(",") if s.strip()]
            series_data = json.loads(series_data_str)
        except (json.JSONDecodeError, AttributeError) as exc:
            log.error("Failed to parse chart data: %s", exc)
            return

        # Clear existing series
        self._plot_widget.clear()
        self._series = []

        if not series_data:
            return

        # Determine Y-axis range from all data
        all_values = []
        for sensor_values in series_data:
            for v in sensor_values:
                try:
                    all_values.append(float(v))
                except (ValueError, TypeError):
                    pass

        if all_values:
            y_min = min(all_values)
            y_max = max(all_values)
            y_range = y_max - y_min
            buffer = max(y_range * 0.1, 1.0)  # At least 1 degree buffer
            self._plot_widget.setYRange(y_min - buffer, y_max + buffer)
        else:
            self._plot_widget.setYRange(-50, 150)

        # X-axis: measurement intervals (0-based index)
        num_points = len(series_data[0]) if series_data else 0
        x_axis = list(range(num_points))

        # Define a set of distinct colors for series
        colors = [
            (255, 87, 87),    # Red
            (66, 165, 245),   # Blue
            (102, 187, 106),  # Green
            (255, 167, 38),   # Orange
            (171, 71, 188),   # Purple
            (0, 188, 212),    # Cyan
            (255, 112, 67),   # Deep Orange
            (121, 85, 72),    # Brown
            (92, 107, 192),   # Indigo
            (48, 125, 117),   # Teal
        ]

        # Add each sensor as a line series
        for i, sensor_name in enumerate(sensor_names):
            if i >= len(series_data):
                break

            sensor_values = series_data[i]
            y_values = []
            for v in sensor_values:
                try:
                    y_values.append(float(v))
                except (ValueError, TypeError):
                    y_values.append(0.0)

            color = colors[i % len(colors)]
            pen = pg.mkPen(color=color, width=1)

            curve = self._plot_widget.plot(x_axis, y_values, name=sensor_name, pen=pen)
            self._series.append(curve)

        self._sensor_names = sensor_names

    @Slot()
    def resetChart(self) -> None:
        """Clear the chart."""
        if self._plot_widget:
            self._plot_widget.clear()
            self._series = []
            self._sensor_names = []

    @Slot()
    def destroyPlotWidget(self) -> None:
        """Destroy the plot widget."""
        if self._plot_window:
            self._plot_window.deleteLater()
            self._plot_window = None
            self._plot_widget = None
            self._series = []

    @Slot(str)
    def updateTrend(self, avgs_json: str) -> None:
        """Plot the running avg temp series."""
        import json
        if not self._plot_widget:
            return
        try:
            avgs = json.loads(avgs_json)
        except (json.JSONDecodeError, TypeError) as exc:
                log.error("Failed to parse trend data: %s", exc)
                return

        if hasattr(self,'_trend_line') and self._trend_line is not None:
            self._plot_widget.removeItem(self._trend_line)

        if not avgs:
            return

        x = list(range(len(avgs)))
        pen = pg.mkPen("#5B9DF5", width=2)
        self._trend_line = self._plot_widget.plot(x, avgs, name="Average", pen=pen)

        # Y-axis range with buffer
        y_min, y_max = min(avgs), max(avgs)
        buf = max((y_max - y_min) * 0.1, 1.0) if y_max > y_min else 5.0
        self._plot_widget.setYRange(y_min - buf, y_max + buf)

    @Slot(str,str,str)
    def updateComparison(self, series_a_json: str, series_b_json: str,path_json: str) -> None:
        """Overlay two runs with DTW warping path connections.

        Args:
            series_a_json: JSON list of floats -- Run Aaverage temps.
            series_b_json: JSON list of floats -- Run B average temps.
            path_json: JSON list of [index_a, index_b] paris.
        """
        import json
        if not self._plot_widget:
            return

        self._plot_widget.clear()

        try:
            a = json.loads(series_a_json)
            b = json.loads(series_b_json)
            path = json.loads(path_json)
        except (json.JSONDecodeError, TypeError) as exc:
            log.error("Failed to parse comparison data: %s", exc)
            return

        pen_a = pg.mkPen("#5B9DF5", width=2) #blue
        pen_b = pg.mkPen("#22C55E", width=2) #green

        self._plot_widget.plot(list(range(len(a))), a, name="Run A", pen=pen_a)
        self._plot_widget.plot(list(range(len(b))), b, name="Run B", pen=pen_b)

        # Draw every 20th warping link as vertical dotted lines
        step = max(1, len(path)//20)
        for i in range (0,len(path),step):
            index_a, index_b = path[i]
            if index_a < len(a) and index_b < len(b):
                line = pg.InfiniteLine(pos=index_a, angle=90, pen=pg.mkPen("#666666", width=1,
                style=Qt.PenStyle.DashLine))
                self._plot_widget.addItem(line)

            self._plot_widget.setTitle("DTW Comparison")
            self._plot_widget.setLabel("left", "Temperature", units="°C")
