"""Feuerwerk-Animation (selbst gezeichnet mit PyQt6)."""

from __future__ import annotations

import math
import random
from dataclasses import dataclass

from PyQt6.QtCore import QPoint, Qt, QTimer
from PyQt6.QtGui import QColor, QPainter, QPen
from PyQt6.QtWidgets import QApplication, QWidget

FEUERWERK_WORDS = (
    "feuerwerk",
    "feuer werke",
    "feuerwerke",
    "firework",
    "fireworks",
    "raketen",
    "rakete",
)

LINES = (
    "BOOM! Feuerwerk für dich!",
    "Juhu! Raketen hoch!",
    "Feuerwerk! Jam liebt Explosionen!",
    "Peng peng! Schönes Feuerwerk!",
    "Das war mein Feuerwerk-Show!",
)


def hears_feuerwerk(text: str) -> bool:
    h = text.lower().replace("jam", " ")
    return any(w in h for w in FEUERWERK_WORDS)


def pick_line() -> str:
    return random.choice(LINES)


@dataclass
class _Particle:
    x: float
    y: float
    vx: float
    vy: float
    life: float
    max_life: float
    color: QColor
    size: float
    trail: bool = False


class FireworksShow(QWidget):
    """Transparentes Vollbild-Overlay mit Raketen und Funken."""

    def __init__(self, jam_center: QPoint | None = None) -> None:
        super().__init__()
        screen = QApplication.primaryScreen()
        if screen is None:
            self._invalid = True
            return
        self.setGeometry(screen.availableGeometry())
        self._invalid = False
        self.setWindowFlags(
            Qt.WindowType.FramelessWindowHint
            | Qt.WindowType.WindowStaysOnTopHint
            | Qt.WindowType.Tool
            | Qt.WindowType.NoDropShadowWindowHint
        )
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
        self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating, True)
        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self._jam_center = jam_center
        self._particles: list[_Particle] = []
        self._tick_n = 0
        self._max_ticks = 300
        self._timer = QTimer(self)
        self._timer.timeout.connect(self._on_tick)
        self._timer.start(33)

    def showEvent(self, event) -> None:
        super().showEvent(event)
        self.raise_()
        for delay in (0, 200, 450, 700, 950):
            QTimer.singleShot(delay, self._spawn_rocket)

    def _spawn_rocket(self) -> None:
        w, h = self.width(), self.height()
        if w < 100 or h < 100:
            return
        if self._jam_center is not None:
            cx = self._jam_center.x() - self.x()
            x = cx + random.uniform(-160, 160)
        else:
            x = random.uniform(w * 0.12, w * 0.88)
        x = max(40.0, min(w - 40.0, x))
        target_y = random.uniform(h * 0.12, h * 0.42)
        self._particles.append(
            _Particle(
                x=x,
                y=float(h - 8),
                vx=random.uniform(-0.35, 0.35),
                vy=random.uniform(-9.5, -7.2),
                life=1.0,
                max_life=1.0,
                color=QColor(255, 220, 120),
                size=3.0,
                trail=True,
            )
        )
        self._particles[-1].target_y = target_y  # type: ignore[attr-defined]

    def _explode(self, x: float, y: float) -> None:
        palette = (
            QColor(255, 80, 120),
            QColor(120, 200, 255),
            QColor(255, 220, 80),
            QColor(180, 120, 255),
            QColor(80, 255, 180),
            QColor(255, 140, 60),
        )
        base = random.choice(palette)
        count = random.randint(42, 68)
        for _ in range(count):
            angle = random.uniform(0, math.tau)
            speed = random.uniform(1.8, 8.5)
            life = random.uniform(0.55, 1.15)
            c = QColor(base)
            c.setRed(min(255, c.red() + random.randint(-30, 40)))
            c.setGreen(min(255, c.green() + random.randint(-30, 40)))
            c.setBlue(min(255, c.blue() + random.randint(-30, 40)))
            self._particles.append(
                _Particle(
                    x=x,
                    y=y,
                    vx=math.cos(angle) * speed,
                    vy=math.sin(angle) * speed,
                    life=life,
                    max_life=life,
                    color=c,
                    size=random.uniform(1.5, 3.2),
                )
            )
        # Zweite kleine Ring-Explosion
        for _ in range(18):
            angle = random.uniform(0, math.tau)
            speed = random.uniform(0.8, 3.5)
            life = random.uniform(0.35, 0.7)
            self._particles.append(
                _Particle(
                    x=x,
                    y=y,
                    vx=math.cos(angle) * speed,
                    vy=math.sin(angle) * speed,
                    life=life,
                    max_life=life,
                    color=QColor(255, 255, 240),
                    size=1.2,
                )
            )

    def _on_tick(self) -> None:
        self._tick_n += 1
        if self._tick_n % 22 == 0 and self._tick_n < self._max_ticks - 40:
            self._spawn_rocket()

        gravity = 0.14
        alive: list[_Particle] = []
        for p in self._particles:
            if p.trail:
                target_y = getattr(p, "target_y", self.height() * 0.3)
                p.x += p.vx
                p.y += p.vy
                p.vy += 0.05
                if p.y <= target_y:
                    self._explode(p.x, p.y)
                    continue
                alive.append(p)
                continue

            p.life -= 0.028
            if p.life <= 0:
                continue
            p.vy += gravity
            p.x += p.vx
            p.y += p.vy
            p.vx *= 0.985
            p.vy *= 0.985
            alive.append(p)

        self._particles = alive
        self.update()

        if self._tick_n >= self._max_ticks and not self._particles:
            self._timer.stop()
            self.close()

    def paintEvent(self, _event) -> None:
        p = QPainter(self)
        p.setRenderHint(QPainter.RenderHint.Antialiasing)
        p.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceOver)
        p.fillRect(self.rect(), Qt.GlobalColor.transparent)

        for part in self._particles:
            alpha = max(0, min(255, int(255 * (part.life / part.max_life))))
            c = QColor(part.color)
            c.setAlpha(alpha)
            if part.trail:
                p.setPen(QPen(c, 2))
                p.drawLine(
                    int(part.x),
                    int(part.y),
                    int(part.x - part.vx * 3),
                    int(part.y - part.vy * 3),
                )
                p.setBrush(c)
                p.drawEllipse(QPoint(int(part.x), int(part.y)), 2, 2)
            else:
                p.setPen(Qt.PenStyle.NoPen)
                p.setBrush(c)
                r = max(1, int(part.size * (0.6 + 0.4 * part.life / part.max_life)))
                p.drawEllipse(QPoint(int(part.x), int(part.y)), r, r)
        p.end()


def launch_fireworks_show(jam_window: QWidget | None = None) -> FireworksShow | None:
    """Feuerwerk über dem Desktop starten."""
    center = None
    if jam_window is not None:
        fg = jam_window.frameGeometry()
        center = fg.center()
    show = FireworksShow(jam_center=center)
    if getattr(show, "_invalid", False) or show.width() < 100:
        show.deleteLater()
        return None
    show.show()
    show.raise_()
    return show
