#!/usr/bin/env python3
"""
Clickable 5‑star rating widget.

Scale: 0 (unrated) … 5 (★★★★★).  Stored internally as stars;
callers convert to/from the iPod 0–100 scale as needed.
"""

import math

from PyQt6.QtCore import Qt, QRectF, pyqtSignal
from PyQt6.QtGui import QPainter, QColor, QPainterPath, QBrush, QPen
from PyQt6.QtWidgets import QWidget


class ClickableStarRating(QWidget):
    """Five clickable stars with hover preview and toggle-to-unrate."""

    rating_changed = pyqtSignal(int)

    STAR_COUNT = 5
    STAR_SIZE = 22          # diameter of each star
    PADDING = 4
    FILLED_COLOR = QColor("#ffb347")   # warm amber
    EMPTY_COLOR = QColor("#555555")
    HOVER_COLOR = QColor("#ffd700")    # gold

    def __init__(self, parent=None):
        super().__init__(parent)
        self._rating = 0
        self._hover_idx = -1
        w = self.STAR_COUNT * self.STAR_SIZE + (self.STAR_COUNT - 1) * self.PADDING
        self.setFixedSize(w, self.STAR_SIZE + 8)
        self.setMouseTracking(True)

    # ── public API ────────────────────────────────────────────────

    def set_rating(self, stars: int) -> None:
        self._rating = max(0, min(self.STAR_COUNT, stars))
        self.update()

    def rating(self) -> int:
        return self._rating

    # ── painting ───────────────────────────────────────────────────

    def paintEvent(self, event):                                # noqa: N802
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        painter.setPen(Qt.PenStyle.NoPen)

        hovering = self._hover_idx >= 0
        for i in range(self.STAR_COUNT):
            if hovering:
                filled = i <= self._hover_idx
            else:
                filled = i < self._rating

            color = (
                self.HOVER_COLOR if hovering and filled
                else self.FILLED_COLOR if filled
                else self.EMPTY_COLOR
            )
            painter.setBrush(QBrush(color))

            cx = self.STAR_SIZE // 2 + i * (self.STAR_SIZE + self.PADDING)
            cy = self.height() // 2
            self._draw_star(painter, cx, cy, self.STAR_SIZE // 2 - 1)

    @staticmethod
    def _draw_star(painter: QPainter, cx: int, cy: int, r: int) -> None:
        """Draw a five-pointed star centred at (cx, cy) with outer radius *r*."""
        path = QPainterPath()
        inner_r = r * 0.4
        points = []
        for i in range(10):
            angle = math.pi / 2 + i * math.pi / 5
            radius = r if i % 2 == 0 else inner_r
            x = cx + radius * math.cos(angle)
            y = cy - radius * math.sin(angle)
            points.append((x, y))
        path.moveTo(points[0][0], points[0][1])
        for x, y in points[1:]:
            path.lineTo(x, y)
        path.closeSubpath()
        painter.drawPath(path)

    # ── mouse interaction ──────────────────────────────────────────

    def _star_at(self, x: int) -> int:
        idx = x // (self.STAR_SIZE + self.PADDING)
        return min(max(idx, 0), self.STAR_COUNT - 1)

    def mousePressEvent(self, event):                            # noqa: N802
        star = self._star_at(int(event.position().x()))
        new_val = star + 1 if self._rating != star + 1 else 0
        self._rating = new_val
        self.rating_changed.emit(new_val)
        self.update()

    def mouseMoveEvent(self, event):                             # noqa: N802
        self._hover_idx = self._star_at(int(event.position().x()))
        self.update()

    def leaveEvent(self, event):                                 # noqa: N802
        self._hover_idx = -1
        self.update()
