import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:pyramid/core/media_cache.dart';

class MxcAvatar extends StatefulWidget {
  final Uri? mxcUri;
  final Client client;
  final double size;
  final BorderRadius? borderRadius;
  final Widget Function(BuildContext) placeholder;

  const MxcAvatar({
    super.key,
    required this.mxcUri,
    required this.client,
    required this.size,
    required this.placeholder,
    this.borderRadius,
  });

  @override
  State<MxcAvatar> createState() => _MxcAvatarState();
}

class _MxcAvatarState extends State<MxcAvatar> {
  late Future<Uint8List?> _future;

  @override
  void initState() {
    super.initState();
    _future = _load();
  }

  @override
  void didUpdateWidget(MxcAvatar old) {
    super.didUpdateWidget(old);
    if (old.mxcUri != widget.mxcUri) _future = _load();
  }

  Future<Uint8List?> _load() async {
    final mxc = widget.mxcUri;
    if (mxc == null) return null;

    final key = 'avatar:${mxc}_${widget.size.round()}';
    // Memory → Disk → Netzwerk. Disk-Treffer machen Avatare nach einem
    // App-Neustart sofort sichtbar statt nachzuladen.
    final hit = await MediaCache.instance.getPersistent(key);
    if (hit != null) return hit;

    final headers = {'authorization': 'Bearer ${widget.client.accessToken}'};

    try {
      // Try thumbnail first; fall back to full download if 404 (server
      // doesn't support on-the-fly thumbnailing, e.g. Continuwuity).
      final thumbUri = await mxc.getThumbnailUri(
        widget.client,
        width: widget.size.round(),
        height: widget.size.round(),
        method: ThumbnailMethod.crop,
      );
      final thumbResp = await widget.client.httpClient.get(thumbUri, headers: headers);
      if (thumbResp.statusCode == 200) {
        await MediaCache.instance.putPersistent(key, thumbResp.bodyBytes);
        return thumbResp.bodyBytes;
      }

      // Thumbnail not available — download full image.
      final dlUri = await mxc.getDownloadUri(widget.client);
      final dlResp = await widget.client.httpClient.get(dlUri, headers: headers);
      if (dlResp.statusCode != 200) return null;
      await MediaCache.instance.putPersistent(key, dlResp.bodyBytes);
      return dlResp.bodyBytes;
    } catch (_) {
      return null;
    }
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Uint8List?>(
      future: _future,
      builder: (context, snap) {
        if (snap.hasData && snap.data != null) {
          return ClipRRect(
            borderRadius: widget.borderRadius ??
                BorderRadius.circular(widget.size / 2),
            child: Image.memory(
              snap.data!,
              width: widget.size,
              height: widget.size,
              fit: BoxFit.cover,
              errorBuilder: (_, _, _) => widget.placeholder(context),
            ),
          );
        }
        return widget.placeholder(context);
      },
    );
  }
}

// Rectangular MXC image (no fixed size, fills parent)
class MxcImage extends StatefulWidget {
  final String mxcUri;
  final Client client;
  final BoxFit fit;
  final double? width;
  final double? height;
  final Widget? placeholder;

  const MxcImage({
    super.key,
    required this.mxcUri,
    required this.client,
    this.fit = BoxFit.cover,
    this.width,
    this.height,
    this.placeholder,
  });

  @override
  State<MxcImage> createState() => _MxcImageState();
}

class _MxcImageState extends State<MxcImage> {
  late Future<Uint8List?> _future;

  @override
  void initState() {
    super.initState();
    _future = _load();
  }

  @override
  void didUpdateWidget(MxcImage old) {
    super.didUpdateWidget(old);
    if (old.mxcUri != widget.mxcUri) {
      // Block-Body: die Closure darf KEIN Future zurückgeben, sonst wirft
      // Flutter "setState() callback returned a Future".
      setState(() {
        _future = _load();
      });
    }
  }

  Future<Uint8List?> _load() async {
    final key = 'img:${widget.mxcUri}';
    final hit = await MediaCache.instance.getPersistent(key);
    if (hit != null) return hit;
    try {
      final uri = Uri.parse(widget.mxcUri);
      final headers = {
        'authorization': 'Bearer ${widget.client.accessToken}',
      };
      // Erst Thumbnail versuchen (spart Bandbreite, v.a. mobil) — Fallback
      // auf Volldownload für Server ohne Thumbnail-Support (Continuwuity).
      try {
        final thumbUri = await uri.getThumbnailUri(
          widget.client,
          width: 800,
          height: 600,
          method: ThumbnailMethod.scale,
        );
        final res = await widget.client.httpClient.get(thumbUri, headers: headers);
        if (res.statusCode == 200) {
          await MediaCache.instance.putPersistent(key, res.bodyBytes);
          return res.bodyBytes;
        }
      } catch (_) {}
      final httpUri = await uri.getDownloadUri(widget.client);
      final res = await widget.client.httpClient.get(httpUri, headers: headers);
      if (res.statusCode != 200) return null;
      final bytes = res.bodyBytes;
      await MediaCache.instance.putPersistent(key, bytes);
      return bytes;
    } catch (_) {
      return null;
    }
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Uint8List?>(
      future: _future,
      builder: (_, snap) {
        if (snap.connectionState == ConnectionState.waiting) {
          return widget.placeholder ?? const SizedBox.shrink();
        }
        if (snap.hasData && snap.data != null) {
          return Image.memory(
            snap.data!,
            fit: widget.fit,
            width: widget.width,
            height: widget.height,
            gaplessPlayback: true,
            errorBuilder: (_, _, _) =>
                widget.placeholder ?? const SizedBox.shrink(),
          );
        }
        return widget.placeholder ?? const SizedBox.shrink();
      },
    );
  }
}
