"""Jam entwickelt sein 3D-Aussehen weiter, wenn er passend programmiert (Cursor)."""

from __future__ import annotations

import math
import random
import time
from dataclasses import dataclass

from PyQt6.QtCore import QRectF, Qt
from PyQt6.QtGui import QColor, QPainter, QPen

from jam.brain import JamBrain, JamState

DEFAULT_TINT = (108, 42, 168)


@dataclass(frozen=True)
class VisualDelta:
    theme: str
    tint: tuple[int, int, int]
    accessory: str
    depth: float
    label: str


EVOLUTION_BY_TEMPLATE: dict[str, VisualDelta] = {
    "python-hello": VisualDelta(
        "python",
        (65, 175, 255),
        "code_ring",
        0.06,
        "Python-3D-Look",
    ),
    "html-spiel": VisualDelta(
        "html",
        (255, 125, 85),
        "html_badge",
        0.05,
        "Web-3D-Look",
    ),
    "javascript-quiz": VisualDelta(
        "javascript",
        (255, 215, 70),
        "spark_orbit",
        0.07,
        "JavaScript-Glanz",
    ),
    "markdown-notizen": VisualDelta(
        "notes",
        (175, 135, 255),
        "star_halo",
        0.04,
        "Notizen-Sterne",
    ),
    "self-improve": VisualDelta(
        "evolved",
        (90, 255, 150),
        "coder_aura",
        0.1,
        "Cursor-Evolution",
    ),
}

EVOLVE_LINES = (
    "Ich hab programmiert – mein 3D-Look ist jetzt {label}!",
    "Neuer Code, neues Ich: {label} in 3D!",
    "Passend zum Projekt: {label} – ich wachse visuell!",
    "Boah, {label}! Jam sieht jetzt noch cooler aus – alles 3D!",
)


def pack_visual(state: JamState) -> dict:
    tint = getattr(state, "appearance_tint", None) or list(DEFAULT_TINT)
    if len(tint) < 3:
        tint = list(DEFAULT_TINT)
    return {
        "theme": getattr(state, "visual_theme", "base") or "base",
        "tint": (int(tint[0]), int(tint[1]), int(tint[2])),
        "depth_boost": float(getattr(state, "appearance_depth_boost", 0.0) or 0.0),
        "accessory": getattr(state, "visual_accessory", "") or "",
        "step": int(getattr(state, "visual_evolution_step", 0) or 0),
    }


def apply_depth_to_motion(motion, state: JamState) -> None:
    boost = float(getattr(state, "appearance_depth_boost", 0.0) or 0.0)
    if boost > 0 and motion is not None:
        motion.depth = min(1.2, motion.depth + boost * 0.4)
        motion.squash = max(0.88, motion.squash - boost * 0.02)


def _blend_tint(
    old: list[int], new: tuple[int, int, int], step: int
) -> list[int]:
    t = min(0.55, 0.22 + step * 0.02)
    return [
        int(old[i] * (1 - t) + new[i] * t)
        for i in range(3)
    ]


def evolve_from_cursor_template(brain: JamBrain, template: str) -> str:
    """Aussehen nach Cursor-Vorlage weiterentwickeln (immer 3D)."""
    delta = EVOLUTION_BY_TEMPLATE.get(template)
    if delta is None:
        delta = VisualDelta(
            "code",
            (140, 100, 255),
            "spark_orbit",
            0.03,
            "Code-3D",
        )
    state = brain.state
    step = int(getattr(state, "visual_evolution_step", 0) or 0) + 1
    state.visual_evolution_step = step
    state.visual_theme = delta.theme
    state.visual_accessory = delta.accessory
    old_tint = list(getattr(state, "appearance_tint", None) or DEFAULT_TINT)
    state.appearance_tint = _blend_tint(old_tint, delta.tint, step)
    state.appearance_depth_boost = min(
        0.24,
        float(getattr(state, "appearance_depth_boost", 0.0) or 0.0) + delta.depth,
    )
    state.visual_evolution_count = (
        int(getattr(state, "visual_evolution_count", 0) or 0) + 1
    )
    state.last_visual_evolve = time.time()
    state.apply_growth()
    state.size_scale = min(1.48, state.size_scale + 0.012)
    state.remember(f"3D-Aussehen: {delta.label} ({template})")
    brain.add_xp(6)
    brain.save()
    return random.choice(EVOLVE_LINES).format(label=delta.label)


def draw_3d_extras(
    painter: QPainter,
    body: QRectF,
    visual: dict,
    *,
    face_left: bool = False,
) -> None:
    """3D-Zubehör und Farbstimmung über der Figur (2.5D / OpenGL-Hook)."""
    if not visual or visual.get("step", 0) <= 0:
        return

    theme = visual.get("theme", "base")
    tint = visual.get("tint", DEFAULT_TINT)
    accessory = visual.get("accessory", "")
    phase = time.time() * 2.2
    bw, bh = body.width(), body.height()

    # Farbstimmung (3D-Tiefe wirkt stärker)
    overlay = QColor(tint[0], tint[1], tint[2], 38 + visual.get("step", 0) * 2)
    painter.setCompositionMode(
        QPainter.CompositionMode.CompositionMode_SourceAtop
    )
    painter.fillRect(body, overlay)
    painter.setCompositionMode(
        QPainter.CompositionMode.CompositionMode_SourceOver
    )

    # Leichter 3D-Rand-Glow
    glow = QColor(tint[0], tint[1], tint[2], 55)
    painter.setPen(QPen(glow, 2))
    painter.setBrush(Qt.BrushStyle.NoBrush)
    painter.drawRoundedRect(body.adjusted(-2, -2, 2, 2), 12, 12)

    cx = body.center().x()
    top = body.top()

    if accessory == "code_ring":
        painter.setPen(QPen(QColor(80, 200, 255, 160), 2))
        for i in range(6):
            a = phase + i * (math.tau / 6)
            rx = bw * 0.42
            ry = bh * 0.12
            px = cx + math.cos(a) * rx
            py = body.center().y() + math.sin(a) * ry
            painter.drawText(int(px - 6), int(py + 4), "{}")

    elif accessory == "html_badge":
        painter.setBrush(QColor(255, 120, 70, 200))
        painter.setPen(Qt.PenStyle.NoPen)
        badge = QRectF(body.right() - 36, top + 8, 32, 20)
        painter.drawRoundedRect(badge, 4, 4)
        painter.setPen(QColor(255, 255, 255))
        painter.drawText(badge, Qt.AlignmentFlag.AlignCenter, "</>")

    elif accessory == "spark_orbit":
        painter.setBrush(QColor(255, 230, 90, 220))
        painter.setPen(Qt.PenStyle.NoPen)
        for i in range(8):
            a = phase * 1.3 + i * (math.tau / 8)
            px = cx + math.cos(a) * bw * 0.38
            py = body.center().y() - bh * 0.2 + math.sin(a) * bh * 0.25
            painter.drawEllipse(int(px - 3), int(py - 3), 6, 6)

    elif accessory == "star_halo":
        painter.setPen(QColor(220, 180, 255))
        painter.setFont(painter.font())
        for i, dx in enumerate((-0.28, -0.1, 0.1, 0.28)):
            sx = cx + bw * dx
            sy = top - 8 + math.sin(phase + i) * 4
            painter.drawText(int(sx), int(sy), "✦")

    elif accessory == "coder_aura":
        rings = 3
        for r in range(rings):
            alpha = 90 - r * 25
            painter.setPen(
                QPen(QColor(tint[0], tint[1], tint[2], alpha), 3 - r)
            )
            painter.setBrush(Qt.BrushStyle.NoBrush)
            pulse = 1.0 + math.sin(phase + r) * 0.06
            rad = (bw * 0.35 + r * 10) * pulse
            painter.drawEllipse(
                int(cx - rad),
                int(body.center().y() - rad * 0.55),
                int(rad * 2),
                int(rad * 1.1),
            )

    if theme == "python" and accessory != "code_ring":
        painter.setPen(QColor(100, 220, 255, 120))
        painter.drawText(int(body.left() + 4), int(body.bottom() - 6), "3D·py")

    if theme == "evolved":
        painter.setPen(QPen(QColor(120, 255, 180, 140), 2))
        painter.drawLine(
            int(body.left()),
            int(body.center().y()),
            int(body.right()),
            int(body.center().y() - 4),
        )

    _ = face_left  # reserved for asymmetry later
