// pyramid_audio_player.dart
// Pyramid · Audio Player
//
// Vier Varianten:
//   AudioPlayerVariant.defaultBars  → Karte mit chunky-bar Waveform (wie HTML "01 · Default")
//   AudioPlayerVariant.gracile      → Karte mit hairline-Waveform (graziler, schicker)
//   AudioPlayerVariant.compact      → Eine Zeile, für In-Chat (Voice-Note Stil)
//   AudioPlayerVariant.mono         → Terminal-Look, Mono-Font, harte Kanten
//
// Verwendung:
//   PyramidAudioPlayer(
//     variant: AudioPlayerVariant.gracile,
//     title: 'test track',
//     artist: 'pyramid · demo',
//     duration: Duration(minutes: 3, seconds: 24),
//     position: Duration(minutes: 1, seconds: 11),
//     playing: true,
//     onTogglePlay: () { ... },
//     onSeek: (Duration to) { ... },
//   );

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'pyramid_theme.dart';

enum AudioPlayerVariant { defaultBars, gracile, compact, mono }

class PyramidAudioPlayer extends StatefulWidget {
  final AudioPlayerVariant variant;
  final String title;
  final String artist;
  final Duration duration;
  final Duration position;
  final bool playing;
  final double volume;            // 0..1
  final VoidCallback? onTogglePlay;
  final ValueChanged<Duration>? onSeek;
  final ValueChanged<double>? onVolumeChange;
  final ValueChanged<double>? onSpeedChange; // 1.0, 1.5, 2.0
  final VoidCallback? onSkipBack;
  final VoidCallback? onSkipForward;
  final VoidCallback? onLike;
  final VoidCallback? onMore;

  const PyramidAudioPlayer({
    super.key,
    this.variant = AudioPlayerVariant.gracile,
    required this.title,
    required this.artist,
    required this.duration,
    required this.position,
    this.playing = false,
    this.volume = 0.7,
    this.onTogglePlay,
    this.onSeek,
    this.onVolumeChange,
    this.onSpeedChange,
    this.onSkipBack,
    this.onSkipForward,
    this.onLike,
    this.onMore,
  });

  @override
  State<PyramidAudioPlayer> createState() => _PyramidAudioPlayerState();
}

class _PyramidAudioPlayerState extends State<PyramidAudioPlayer> {
  double speed = 1.0;
  static const speeds = [1.0, 1.5, 2.0];

  double get progress => widget.duration.inMilliseconds == 0
      ? 0
      : widget.position.inMilliseconds / widget.duration.inMilliseconds;

  String _fmt(Duration d) {
    final m = d.inMinutes;
    final s = (d.inSeconds % 60).toString().padLeft(2, '0');
    return '$m:$s';
  }

  @override
  Widget build(BuildContext context) {
    if (widget.variant == AudioPlayerVariant.compact) return _buildCompact();
    return _buildCard();
  }

  // ─── Compact (in-chat) ─────────────────────────────────────────────
  Widget _buildCompact() {
    return Container(
      padding: const EdgeInsets.fromLTRB(12, 12, 14, 12),
      decoration: BoxDecoration(
        color: PyramidTheme.bg1,
        border: Border.all(color: PyramidTheme.border),
        borderRadius: BorderRadius.circular(PyramidTheme.rLg),
      ),
      child: Row(
        children: [
          _Artwork(size: 36),
          const SizedBox(width: 10),
          Flexible(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(widget.title, style: PyramidTheme.title.copyWith(fontSize: 13), overflow: TextOverflow.ellipsis),
                Text(widget.artist, style: PyramidTheme.subtitle.copyWith(fontSize: 11), overflow: TextOverflow.ellipsis),
              ],
            ),
          ),
          const SizedBox(width: 12),
          _PlayButton(playing: widget.playing, onTap: widget.onTogglePlay, size: 32),
          const SizedBox(width: 12),
          Text(_fmt(widget.position), style: PyramidTheme.mono),
          const SizedBox(width: 8),
          Expanded(child: _Scrubber(progress: progress, onSeek: _seekFromFraction)),
          const SizedBox(width: 8),
          Text(_fmt(widget.duration), style: PyramidTheme.mono),
        ],
      ),
    );
  }

  // ─── Default / Gracile / Mono ──────────────────────────────────────
  Widget _buildCard() {
    final isMono = widget.variant == AudioPlayerVariant.mono;
    final radius = isMono ? 0.0 : PyramidTheme.rLg;

    return Container(
      padding: const EdgeInsets.fromLTRB(20, 18, 20, 18),
      decoration: BoxDecoration(
        color: isMono ? PyramidTheme.bg0 : PyramidTheme.bg1,
        border: Border.all(color: isMono ? PyramidTheme.borderStrong : PyramidTheme.border),
        borderRadius: BorderRadius.circular(radius),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          // Head
          Row(
            children: [
              _Artwork(size: 48, mono: isMono),
              const SizedBox(width: 14),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(
                      isMono ? widget.title.toUpperCase() : widget.title,
                      style: isMono
                          ? PyramidTheme.monoStrong.copyWith(fontSize: 12, color: PyramidTheme.fg, letterSpacing: 0.3)
                          : PyramidTheme.title,
                      overflow: TextOverflow.ellipsis,
                    ),
                    Text(
                      isMono ? widget.artist.toUpperCase() : widget.artist,
                      style: isMono
                          ? PyramidTheme.mono.copyWith(fontSize: 10, letterSpacing: 0.8)
                          : PyramidTheme.subtitle,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ],
                ),
              ),
              _IconBtn(icon: Icons.favorite_border, onTap: widget.onLike),
              _IconBtn(icon: Icons.more_horiz, onTap: widget.onMore),
            ],
          ),
          const SizedBox(height: 14),

          // Waveform
          GestureDetector(
            onTapDown: (d) => _seekFromTap(d.localPosition.dx, context.size?.width ?? 1),
            child: SizedBox(
              height: widget.variant == AudioPlayerVariant.gracile ? 44 : 40,
              child: widget.variant == AudioPlayerVariant.gracile
                  ? CustomPaint(painter: _GracileWavePainter(progress: progress), size: Size.infinite)
                  : CustomPaint(painter: _ChunkyBarsPainter(progress: progress), size: Size.infinite),
            ),
          ),
          const SizedBox(height: 8),

          // Times
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(_fmt(widget.position), style: PyramidTheme.mono),
              Text('−${_fmt(widget.duration - widget.position)}', style: PyramidTheme.mono),
            ],
          ),
          const SizedBox(height: 14),

          // Controls row
          Row(
            children: [
              _IconBtn(icon: Icons.volume_up_outlined, onTap: null),
              const SizedBox(width: 8),
              SizedBox(
                width: 72,
                child: _MiniBar(value: widget.volume),
              ),
              const Spacer(),
              _IconBtn(icon: Icons.replay_10, onTap: widget.onSkipBack), // ≈ −15 (Material has 10/30; close enough)
              const SizedBox(width: 4),
              _PlayButton(playing: widget.playing, onTap: widget.onTogglePlay, size: 40, mono: isMono),
              const SizedBox(width: 4),
              _IconBtn(icon: Icons.forward_10, onTap: widget.onSkipForward),
              const Spacer(),
              ...speeds.map((s) => _SpeedPill(
                    label: '${s == s.toInt() ? s.toInt() : s}x',
                    active: speed == s,
                    mono: isMono,
                    onTap: () {
                      setState(() => speed = s);
                      widget.onSpeedChange?.call(s);
                    },
                  )),
            ],
          ),
        ],
      ),
    );
  }

  void _seekFromFraction(double f) =>
      widget.onSeek?.call(Duration(milliseconds: (widget.duration.inMilliseconds * f).round()));

  void _seekFromTap(double x, double w) {
    if (w <= 0) return;
    _seekFromFraction((x / w).clamp(0.0, 1.0));
  }
}

// ───────────────────────── Building blocks ─────────────────────────

class _Artwork extends StatelessWidget {
  final double size;
  final bool mono;
  const _Artwork({required this.size, this.mono = false});
  @override
  Widget build(BuildContext context) {
    return Container(
      width: size,
      height: size,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(mono ? 0 : PyramidTheme.rSm),
        gradient: mono
            ? null
            : LinearGradient(
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
                colors: [
                  HSLColor.fromAHSL(1, PyramidTheme.accentH, PyramidTheme.accentS / 100, (PyramidTheme.accentL - 10) / 100).toColor(),
                  HSLColor.fromAHSL(1, PyramidTheme.accentH + 30, PyramidTheme.accentS / 100, (PyramidTheme.accentL - 20) / 100).toColor(),
                ],
              ),
        color: mono ? PyramidTheme.accent : null,
      ),
      child: Center(
        child: CustomPaint(
          size: Size(size * 0.45, size * 0.42),
          painter: _TrianglePainter(color: mono ? PyramidTheme.accentFg : Colors.white.withOpacity(0.9)),
        ),
      ),
    );
  }
}

class _TrianglePainter extends CustomPainter {
  final Color color;
  _TrianglePainter({required this.color});
  @override
  void paint(Canvas canvas, Size s) {
    final p = Paint()..color = color;
    final path = Path()
      ..moveTo(s.width / 2, 0)
      ..lineTo(s.width, s.height)
      ..lineTo(0, s.height)
      ..close();
    canvas.drawPath(path, p);
  }
  @override
  bool shouldRepaint(_) => false;
}

class _PlayButton extends StatelessWidget {
  final bool playing;
  final VoidCallback? onTap;
  final double size;
  final bool mono;
  const _PlayButton({required this.playing, this.onTap, this.size = 40, this.mono = false});
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        width: size,
        height: size,
        decoration: BoxDecoration(
          color: PyramidTheme.accent,
          shape: mono ? BoxShape.rectangle : BoxShape.circle,
          boxShadow: [BoxShadow(color: PyramidTheme.accentGlow, blurRadius: 18, offset: const Offset(0, 4))],
        ),
        child: Icon(playing ? Icons.pause : Icons.play_arrow, color: PyramidTheme.accentFg, size: size * 0.45),
      ),
    );
  }
}

class _IconBtn extends StatelessWidget {
  final IconData icon;
  final VoidCallback? onTap;
  const _IconBtn({required this.icon, this.onTap});
  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onTap,
      borderRadius: BorderRadius.circular(PyramidTheme.rSm),
      child: Container(
        width: 32, height: 32,
        alignment: Alignment.center,
        child: Icon(icon, size: 18, color: PyramidTheme.fgMuted),
      ),
    );
  }
}

class _SpeedPill extends StatelessWidget {
  final String label;
  final bool active;
  final bool mono;
  final VoidCallback? onTap;
  const _SpeedPill({required this.label, this.active = false, this.mono = false, this.onTap});
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        margin: const EdgeInsets.symmetric(horizontal: 1),
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
        decoration: BoxDecoration(
          color: active ? PyramidTheme.accentSoft : PyramidTheme.bg2,
          borderRadius: BorderRadius.circular(mono ? 0 : PyramidTheme.rSm),
          border: Border.all(color: active ? PyramidTheme.accentSoft : Colors.transparent),
        ),
        child: Text(label, style: TextStyle(
          fontFamily: PyramidTheme.fontMono, fontSize: 11, fontWeight: FontWeight.w500,
          color: active ? PyramidTheme.accent : PyramidTheme.fgMuted,
          letterSpacing: 0.2,
        )),
      ),
    );
  }
}

class _Scrubber extends StatelessWidget {
  final double progress; // 0..1
  final ValueChanged<double>? onSeek;
  const _Scrubber({required this.progress, this.onSeek});
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (_, c) {
      return GestureDetector(
        onTapDown: (d) => onSeek?.call((d.localPosition.dx / c.maxWidth).clamp(0.0, 1.0)),
        onPanUpdate: (d) => onSeek?.call(((d.localPosition.dx) / c.maxWidth).clamp(0.0, 1.0)),
        child: SizedBox(
          height: 14,
          child: Stack(alignment: Alignment.centerLeft, children: [
            Container(height: 3, decoration: BoxDecoration(color: PyramidTheme.bg3, borderRadius: BorderRadius.circular(2))),
            FractionallySizedBox(
              widthFactor: progress,
              child: Container(height: 3, decoration: BoxDecoration(color: PyramidTheme.accent, borderRadius: BorderRadius.circular(2))),
            ),
          ]),
        ),
      );
    });
  }
}

class _MiniBar extends StatelessWidget {
  final double value;
  const _MiniBar({required this.value});
  @override
  Widget build(BuildContext context) {
    return Stack(alignment: Alignment.centerLeft, children: [
      Container(height: 3, decoration: BoxDecoration(color: PyramidTheme.bg3, borderRadius: BorderRadius.circular(2))),
      FractionallySizedBox(
        widthFactor: value,
        child: Container(height: 3, decoration: BoxDecoration(color: PyramidTheme.fgMuted, borderRadius: BorderRadius.circular(2))),
      ),
    ]);
  }
}

// ───────────────────────── Wave painters ─────────────────────────

class _ChunkyBarsPainter extends CustomPainter {
  final double progress;
  static const int count = 56;
  static final List<double> _heights = _seed();
  static List<double> _seed() {
    final r = math.Random(7);
    return List.generate(count, (i) {
      final env = math.sin((i / count) * math.pi) * 0.6 + 0.4;
      return 0.2 + r.nextDouble() * 0.8 * env;
    });
  }

  _ChunkyBarsPainter({required this.progress});
  @override
  void paint(Canvas canvas, Size s) {
    final gap = 2.0;
    final barW = (s.width - gap * (count - 1)) / count;
    final cursor = (progress * count).floor();
    for (int i = 0; i < count; i++) {
      final h = _heights[i] * s.height;
      final x = i * (barW + gap);
      final y = (s.height - h) / 2;
      final played = i < cursor;
      final isCursor = i == cursor;
      final paint = Paint()
        ..color = isCursor
            ? PyramidTheme.fg
            : played
                ? PyramidTheme.accent
                : PyramidTheme.bg3;
      final r = RRect.fromRectAndRadius(Rect.fromLTWH(x, y, barW, h), const Radius.circular(1));
      canvas.drawRRect(r, paint);
    }
  }
  @override
  bool shouldRepaint(_ChunkyBarsPainter old) => old.progress != progress;
}

class _GracileWavePainter extends CustomPainter {
  final double progress;
  static const int count = 72;
  static final List<double> _heights = _seed();
  static List<double> _seed() {
    final r = math.Random(11);
    return List.generate(count, (i) {
      final env = math.sin((i / count) * math.pi) * 0.55 + 0.45;
      return 0.18 + r.nextDouble() * 0.82 * env;
    });
  }

  _GracileWavePainter({required this.progress});
  @override
  void paint(Canvas canvas, Size s) {
    final cy = s.height / 2;
    // Baseline
    canvas.drawLine(
      Offset(0, cy), Offset(s.width, cy),
      Paint()..color = PyramidTheme.border..strokeWidth = 0.6,
    );
    final step = s.width / (count - 1);
    for (int i = 0; i < count; i++) {
      final x = i * step;
      final h = _heights[i] * s.height * 0.88;
      final played = (x / s.width) <= progress;
      final p = Paint()
        ..color = (played ? PyramidTheme.accent : PyramidTheme.fgDim).withOpacity(played ? 0.95 : 0.42)
        ..strokeWidth = 1.0
        ..strokeCap = StrokeCap.round;
      canvas.drawLine(Offset(x, cy - h / 2), Offset(x, cy + h / 2), p);
    }
    // Playhead
    final cx = progress * s.width;
    canvas.drawLine(Offset(cx, 4), Offset(cx, s.height - 4),
        Paint()..color = PyramidTheme.fg..strokeWidth = 1.2);
    canvas.drawCircle(Offset(cx, cy), 2.4, Paint()..color = PyramidTheme.accent);
  }
  @override
  bool shouldRepaint(_GracileWavePainter old) => old.progress != progress;
}
