"""
Rain overlay effect with fully customizable physics.
تأثير المطر مع فيزياء قابلة للتخصيص بالكامل.

Master Mode Example:
    >>> from sirabody import RainOverlay
    >>> rain = RainOverlay(
    ...     parent=my_widget,
    ...     rain_color=(220, 245, 255),   # لون قطرات المطر (RGB)
    ...     rain_speed=(4, 10),           # سرعة القطرات (min, max)
    ...     rain_count=200,               # عدد القطرات
    ...     rain_length=(10, 40),         # طول القطرات (min, max)
    ...     splash_enabled=True,          # تفعيل الرذاذ
    ...     mouse_interaction=True,       # تفاعل الماوس
    ...     collision_enabled=True,       # تصادم مع الأزرار
    ...     physics_fps=120,              # سرعة الفيزياء
    ...     wind_strength=0.3,            # قوة الرياح عند تحريك النافذة
    ... )
"""

import random
import math
from PySide6.QtWidgets import QWidget, QPushButton, QFrame
from PySide6.QtCore import Qt, QTimer, QPoint, QElapsedTimer, QRect, QRectF, QLineF
from PySide6.QtGui import QPainter, QColor, QBrush, QPen, QCursor, QLinearGradient


class SplashParticle:
    """جزيئة رذاذ عند اصطدام قطرة المطر."""
    def __init__(self, x, y, inertia, color=(200, 240, 255)):
        self.x = x
        self.y = y
        self.prev_x = x
        self.prev_y = y
        self.vx = random.uniform(-2, 2) + inertia * 0.5
        self.vy = random.uniform(-3, -6)
        self.alpha = 255.0
        self.life_speed = random.uniform(8.0, 15.0)
        self.color = color

    def update(self, dt):
        self.prev_x = self.x
        self.prev_y = self.y
        self.vy += 9.8 * dt * 2.5
        self.x += self.vx * 60 * dt
        self.y += self.vy * 60 * dt
        self.alpha -= self.life_speed
        if self.alpha < 0:
            self.alpha = 0
        return self.alpha > 0


class RainDrop:
    """قطرة مطر واحدة."""
    def __init__(self, width, height, speed_range=(6, 14), length_range=(15, 30)):
        self.speed_range = speed_range
        self.length_range = length_range
        self.reset(width, height, first_spawn=True)

    def reset(self, width, height, first_spawn=False):
        self.x = random.randint(-400, width + 400)
        self.y = random.randint(-height, 0) if first_spawn else random.randint(-100, -10)
        self.prev_x = self.x
        self.prev_y = self.y
        self.z = random.randint(1, 3)
        self.length = random.randint(self.length_range[0], self.length_range[1])
        base_speed = random.randint(self.speed_range[0], self.speed_range[1])
        self.speed = base_speed * (1.3 if self.z == 3 else 1.0)
        self.alpha = random.randint(100, 200)
        self.width = max(1, self.z / 2)

    def update(self, width, height, dx_inertia, dt):
        self.prev_x = self.x
        self.prev_y = self.y
        self.y += self.speed * 60 * dt
        self.x += dx_inertia

        margin = 400
        total_w = width + 2 * margin
        if self.x < -margin:
            self.x += total_w
            self.prev_x += total_w
        elif self.x > width + margin:
            self.x -= total_w
            self.prev_x -= total_w

        if self.y > height + 50:
            self.reset(width, height)
            return "reset"
        return "normal"


class RainOverlay(QWidget):
    """طبقة تأثير المطر مع فيزياء واقعية وقابلة للتخصيص بالكامل.

    Args:
        parent: العنصر الأب
        is_foreground (bool): إذا كانت الطبقة أمامية (تتفاعل مع العناصر)
        rain_count (int): عدد قطرات المطر (افتراضي: 100)
        rain_color (tuple): لون القطرات بصيغة RGB (افتراضي: (220, 245, 255) = أزرق ثلجي)
        splash_color (tuple): لون الرذاذ بصيغة RGB (افتراضي: (200, 240, 255))
        rain_speed (tuple): سرعة القطرات (min, max) (افتراضي: (6, 14))
        rain_length (tuple): طول القطرات (min, max) (افتراضي: (15, 30))
        splash_enabled (bool): تفعيل تأثير الرذاذ عند الاصطدام (افتراضي: True)
        mouse_interaction (bool): تفعيل تفاعل الماوس مع القطرات (افتراضي: True)
        mouse_radius (int): نصف قطر تفاعل الماوس بالبكسل (افتراضي: 25)
        collision_enabled (bool): تفعيل تصادم القطرات مع الأزرار (افتراضي: True)
        physics_fps (int): سرعة محرك الفيزياء (افتراضي: 120)
        wind_strength (float): قوة تأثير الرياح عند تحريك النافذة (افتراضي: 0.3)
        sparkle_enabled (bool): تفعيل لمعان رأس القطرة (افتراضي: True)
        sparkle_color (tuple): لون اللمعان بصيغة RGBA (افتراضي: (255, 255, 255, 240))

    Example (Master Mode):
        >>> rain = RainOverlay(
        ...     parent=widget,
        ...     rain_color=(255, 100, 100),   # مطر أحمر!
        ...     rain_count=300,               # مطر غزير
        ...     rain_speed=(10, 25),          # سريع
        ...     splash_enabled=True,
        ...     mouse_interaction=True,
        ... )
    """
    def __init__(self, parent=None, is_foreground=True, rain_count=100,
                 rain_color=(220, 245, 255), splash_color=(200, 240, 255),
                 rain_speed=(6, 14), rain_length=(15, 30),
                 splash_enabled=True, mouse_interaction=True, mouse_radius=25,
                 collision_enabled=True, physics_fps=120,
                 wind_strength=0.3, sparkle_enabled=True,
                 sparkle_color=(255, 255, 255, 240),
                 # Legacy support
                 particle_count=None):
        super().__init__(parent)
        self.setAttribute(Qt.WA_TransparentForMouseEvents)

        # Legacy: particle_count overrides rain_count
        if particle_count is not None:
            rain_count = particle_count

        # Store config
        self.is_foreground = is_foreground
        self.rain_color = rain_color
        self.splash_color = splash_color
        self.rain_speed = rain_speed
        self.rain_length = rain_length
        self.splash_enabled = splash_enabled
        self.mouse_interaction = mouse_interaction
        self.mouse_radius = mouse_radius
        self.mouse_radius_sq = mouse_radius * mouse_radius
        self.collision_enabled = collision_enabled
        self.wind_strength = wind_strength
        self.sparkle_enabled = sparkle_enabled
        self.sparkle_color_val = QColor(*sparkle_color) if len(sparkle_color) == 4 else QColor(*sparkle_color, 240)

        self.particles = []

        self.timer = QTimer(self)
        self.timer.timeout.connect(self._game_loop)
        self.timer.start(1)

        self.elapsed_timer = QElapsedTimer()
        self.elapsed_timer.start()
        self.last_time_ns = self.elapsed_timer.nsecsElapsed()
        self.accumulator = 0.0
        self.physics_dt = 1.0 / physics_fps

        self.drops = [RainDrop(800, 600, speed_range=rain_speed, length_range=rain_length)
                      for _ in range(rain_count)]

        self.current_inertia = 0
        self.target_inertia = 0
        self.collision_rects = []

        self.frame_count = 0
        self.fps_accum = 0
        self.fps = 0

    # ─── Public API ───────────────────────────────────────────

    def set_rain_color(self, r, g, b):
        """تغيير لون المطر أثناء التشغيل.

        Args:
            r (int): أحمر (0-255)
            g (int): أخضر (0-255)
            b (int): أزرق (0-255)

        Example:
            >>> rain.set_rain_color(255, 0, 0)  # مطر أحمر
        """
        self.rain_color = (r, g, b)

    def set_splash_color(self, r, g, b):
        """تغيير لون الرذاذ أثناء التشغيل."""
        self.splash_color = (r, g, b)

    def set_rain_speed(self, min_speed, max_speed):
        """تغيير سرعة المطر أثناء التشغيل.

        Args:
            min_speed (int): أقل سرعة
            max_speed (int): أعلى سرعة
        """
        self.rain_speed = (min_speed, max_speed)
        for d in self.drops:
            d.speed_range = (min_speed, max_speed)

    def set_rain_count(self, count):
        """تغيير عدد القطرات أثناء التشغيل.

        Args:
            count (int): العدد الجديد
        """
        current = len(self.drops)
        if count > current:
            for _ in range(count - current):
                self.drops.append(RainDrop(self.width() or 800, self.height() or 600,
                                          speed_range=self.rain_speed, length_range=self.rain_length))
        elif count < current:
            self.drops = self.drops[:count]

    def set_wind(self, strength):
        """تغيير قوة الرياح.

        Args:
            strength (float): القوة (0.0 = بدون رياح, 1.0 = قوي)
        """
        self.wind_strength = strength

    def apply_inertia(self, force_x):
        """Apply inertia from window movement."""
        clamped_force = max(-15, min(15, force_x * self.wind_strength))
        self.target_inertia += clamped_force
        self.target_inertia = max(-30, min(30, self.target_inertia))

    # ─── Internal ─────────────────────────────────────────────

    def _integrate_physics(self, dt):
        lerp_speed = 5.0
        factor = 1.0 - math.exp(-lerp_speed * dt)
        self.current_inertia += (self.target_inertia - self.current_inertia) * factor

        decay_speed = 3.0
        decay_factor = 1.0 - math.exp(-decay_speed * dt)
        self.target_inertia -= self.target_inertia * decay_factor

        if abs(self.current_inertia) < 0.1:
            self.current_inertia = 0
        if abs(self.target_inertia) < 0.1:
            self.target_inertia = 0

        if self.collision_enabled:
            self._cache_collision_rects()

        local_mouse = self.mapFromGlobal(QCursor.pos())
        mouse_active = self.mouse_interaction and self.rect().contains(local_mouse)
        real_inertia_x = -self.current_inertia * 0.2

        for d in self.drops:
            if self.is_foreground:
                hit = False

                # UI collision
                if self.collision_enabled:
                    p_check = QPoint(int(d.x), int(d.y + d.length))
                    for r in self.collision_rects:
                        if r.contains(p_check):
                            hit = True
                            if self.splash_enabled:
                                for _ in range(random.randint(2, 4)):
                                    self.particles.append(
                                        SplashParticle(d.x, d.y + d.length, real_inertia_x, self.splash_color))
                            d.reset(self.width(), self.height())
                            break

                # Mouse collision
                if not hit and mouse_active:
                    dx = d.x - local_mouse.x()
                    dy = (d.y + d.length) - local_mouse.y()
                    dist_sq = dx * dx + dy * dy
                    if dist_sq < self.mouse_radius_sq:
                        hit = True
                        if self.splash_enabled:
                            for _ in range(random.randint(5, 8)):
                                self.particles.append(
                                    SplashParticle(d.x, d.y + d.length, real_inertia_x * 2, self.splash_color))
                        d.reset(self.width(), self.height())

                if not hit:
                    d.update(self.width(), self.height(), real_inertia_x, dt)
            else:
                d.update(self.width(), self.height(), real_inertia_x, dt)

        if self.splash_enabled:
            for p in self.particles[:]:
                p.update(dt)
                if p.alpha <= 0:
                    self.particles.remove(p)

    def _cache_collision_rects(self):
        targets = []
        cw = self.parentWidget()
        if cw:
            for child in cw.children():
                if isinstance(child, QFrame) and child.y() > (self.height() * 0.8):
                    targets.append(child.geometry())
                if isinstance(child, QFrame) and child.y() < (self.height() * 0.8):
                    for sub in child.children():
                        if isinstance(sub, QPushButton) and sub.isVisible():
                            abs_pos = sub.mapTo(cw, QPoint(0, 0))
                            rect = QRect(abs_pos, sub.size())
                            targets.append(rect)
        self.collision_rects = targets

    def _game_loop(self):
        current_time = self.elapsed_timer.nsecsElapsed()
        dt_ns = current_time - self.last_time_ns
        self.last_time_ns = current_time
        frame_time = dt_ns / 1_000_000_000.0
        if frame_time > 0.1:
            frame_time = 0.1

        self.accumulator += frame_time
        self.fps_accum += frame_time
        self.frame_count += 1
        if self.fps_accum >= 1.0:
            self.fps = self.frame_count
            self.frame_count = 0
            self.fps_accum = 0

        while self.accumulator >= self.physics_dt:
            self._integrate_physics(self.physics_dt)
            self.accumulator -= self.physics_dt

        self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setPen(Qt.NoPen)

        alpha_blend = self.accumulator / self.physics_dt
        if alpha_blend > 1.0:
            alpha_blend = 1.0

        tilt = max(-15, min(15, -self.current_inertia * 0.3))
        rc = self.rain_color

        for d in self.drops:
            draw_x = d.prev_x * (1.0 - alpha_blend) + d.x * alpha_blend
            draw_y = d.prev_y * (1.0 - alpha_blend) + d.y * alpha_blend

            p_head = QPoint(draw_x + tilt, draw_y)
            p_tail = QPoint(draw_x, draw_y - d.length)

            grad = QLinearGradient(p_head, p_tail)
            c_head = QColor(rc[0], rc[1], rc[2], int(d.alpha))
            c_tail = QColor(rc[0], rc[1], rc[2], 0)
            grad.setColorAt(0.0, c_head)
            grad.setColorAt(1.0, c_tail)

            pen = QPen(QBrush(grad), d.width)
            pen.setCapStyle(Qt.RoundCap)
            painter.setPen(pen)
            painter.drawLine(QLineF(p_head, p_tail))

            if self.sparkle_enabled and d.width > 1.5:
                painter.setPen(QPen(self.sparkle_color_val, d.width))
                painter.drawPoint(p_head)

        if self.splash_enabled:
            painter.setPen(Qt.NoPen)
            for p in self.particles:
                px = p.prev_x * (1.0 - alpha_blend) + p.x * alpha_blend
                py = p.prev_y * (1.0 - alpha_blend) + p.y * alpha_blend
                color = QColor(p.color[0], p.color[1], p.color[2], int(p.alpha))
                painter.setBrush(color)
                r = 1.5
                painter.drawEllipse(QRectF(px - r, py - r, r * 2, r * 2))
