import 'dart:math' as math;

import 'package:flutter/material.dart';

import '../../game/models/season.dart';
import '../theme/palette.dart';

/// Set true in tests (flutter_test_config.dart): looping ambience would make
/// pumpAndSettle spin forever.
bool debugDisableLoopingAnimations = false;

/// Full-screen season ambience: vertical gradient plus drifting particles
/// (petals for season 1). Pure procedural — no image assets required; an AI
/// illustration layer can be added on top later without touching callers.
class SeasonBackground extends StatefulWidget {
  const SeasonBackground({super.key, required this.theme});

  final SeasonTheme theme;

  @override
  State<SeasonBackground> createState() => _SeasonBackgroundState();
}

class _SeasonBackgroundState extends State<SeasonBackground>
    with SingleTickerProviderStateMixin {
  late final AnimationController _drift = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 18),
  );

  late ThemeColors _colors = ThemeColors(widget.theme);

  @override
  void initState() {
    super.initState();
    if (!debugDisableLoopingAnimations) _drift.repeat();
  }

  @override
  void didUpdateWidget(SeasonBackground old) {
    super.didUpdateWidget(old);
    if (old.theme != widget.theme) _colors = ThemeColors(widget.theme);
  }

  @override
  void dispose() {
    _drift.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: AnimatedBuilder(
        animation: _drift,
        builder: (context, _) => CustomPaint(
          size: Size.infinite,
          painter: _AmbiencePainter(colors: _colors, t: _drift.value),
        ),
      ),
    );
  }
}

class _AmbiencePainter extends CustomPainter {
  const _AmbiencePainter({required this.colors, required this.t});

  final ThemeColors colors;
  final double t;

  static const _particles = 9;

  // Deterministic pseudo-random in [0, 1) from an index.
  static double _hash(int i, double salt) {
    final v = math.sin(i * 12.9898 + salt) * 43758.5453;
    return v - v.floorToDouble();
  }

  @override
  void paint(Canvas canvas, Size size) {
    final rect = Offset.zero & size;
    canvas.drawRect(
      rect,
      Paint()
        ..shader = LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: colors.gradient,
          stops: colors.gradient.length == 3
              ? const [0.0, 0.55, 1.0]
              : null,
        ).createShader(rect),
    );

    if (colors.particleType == 'none') return;
    for (var i = 0; i < _particles; i++) {
      final speed = 0.5 + _hash(i, 1) * 0.6;
      final phase = _hash(i, 2);
      final fall = (t * speed + phase) % 1.15 - 0.075;
      final x = (_hash(i, 3) +
              0.05 * math.sin(t * 2 * math.pi + i * 1.7)) *
          size.width;
      final y = fall * size.height;
      final scale = 7 + _hash(i, 4) * 9;
      final angle = t * 2 * math.pi * (0.4 + _hash(i, 5)) + i;
      _paintParticle(canvas, Offset(x, y), scale, angle);
    }
  }

  void _paintParticle(Canvas canvas, Offset c, double s, double angle) {
    canvas.save();
    canvas.translate(c.dx, c.dy);
    canvas.rotate(angle);
    final paint = Paint();
    switch (colors.particleType) {
      case 'snow':
        paint.color = Colors.white.withValues(alpha: 0.35);
        canvas.drawCircle(Offset.zero, s * 0.4, paint);
      case 'leaves':
        paint.color = colors.accent.withValues(alpha: 0.35);
        canvas.drawOval(
            Rect.fromCenter(center: Offset.zero, width: s, height: s * 0.55),
            paint);
      default: // petals
        paint.color = colors.accent.withValues(alpha: 0.30);
        canvas.drawOval(
            Rect.fromCenter(
                center: Offset(s * 0.18, 0), width: s, height: s * 0.62),
            paint);
        canvas.drawOval(
            Rect.fromCenter(
                center: Offset(-s * 0.18, 0), width: s, height: s * 0.62),
            paint);
    }
    canvas.restore();
  }

  @override
  bool shouldRepaint(_AmbiencePainter old) =>
      old.t != t || old.colors != colors;
}
