import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../providers/emby_headers_provider.dart';
import '../../providers/session_provider.dart';

class SmartImage extends ConsumerStatefulWidget {
  final String? url;
  final List<String> fallbackUrls;
  final BoxFit fit;
  final Alignment alignment;
  final double? aspectRatio;
  final double borderRadius;
  final IconData? fallbackIcon;
  final VoidCallback? onLoad;
  final VoidCallback? onError;
  final Map<String, String>? httpHeaders;
  final Map<String, String>? Function(String url)? resolveHttpHeaders;


  final int? memCacheWidth;


  final Widget? placeholder;

  const SmartImage({
    super.key,
    required this.url,
    this.fallbackUrls = const [],
    this.fit = BoxFit.cover,
    this.alignment = Alignment.center,
    this.aspectRatio,
    this.borderRadius = 8,
    this.fallbackIcon = Icons.image_outlined,
    this.onLoad,
    this.onError,
    this.httpHeaders,
    this.resolveHttpHeaders,
    this.memCacheWidth,
    this.placeholder,
  });

  @override
  ConsumerState<SmartImage> createState() => _SmartImageState();
}

class _SmartImageState extends ConsumerState<SmartImage> {
  List<String> _displayedUrls = const [];
  int _failedCount = 0;
  String? _pendingPrimaryUrl;
  int? _lastMemCacheWidth;

  String? _embyBaseUrl;
  Map<String, String>? _embyHeaders;

  List<String> _resolveAllUrls() {
    final urls = <String>[];
    if (widget.url != null) urls.add(widget.url!);
    urls.addAll(widget.fallbackUrls);
    return urls;
  }

  Map<String, String>? _headersFor(String url) {
    if (widget.resolveHttpHeaders != null) {
      return widget.resolveHttpHeaders!(url);
    }
    if (widget.httpHeaders != null) return widget.httpHeaders;
    final base = _embyBaseUrl;
    if (base != null && url.startsWith(base)) return _embyHeaders;
    return null;
  }

  @override
  void initState() {
    super.initState();
    _displayedUrls = _resolveAllUrls();
  }

  @override
  void didUpdateWidget(covariant SmartImage oldWidget) {
    super.didUpdateWidget(oldWidget);
    final newAll = _resolveAllUrls();
    final newPrimary = newAll.isEmpty ? null : newAll.first;
    final currentPrimary = _displayedUrls.isEmpty ? null : _displayedUrls.first;

    if (newPrimary == null) {
      if (_displayedUrls.isNotEmpty || _pendingPrimaryUrl != null) {
        setState(() {
          _displayedUrls = const [];
          _failedCount = 0;
          _pendingPrimaryUrl = null;
        });
      }
      return;
    }

    if (currentPrimary == null) {
      setState(() {
        _displayedUrls = newAll;
        _failedCount = 0;
        _pendingPrimaryUrl = null;
      });
      return;
    }

    if (newPrimary == currentPrimary) {
      _displayedUrls = newAll;
      return;
    }

    _preloadAndSwap(newPrimary, newAll);
  }

  Future<void> _preloadAndSwap(String newUrl, List<String> newAll) async {
    if (_pendingPrimaryUrl == newUrl) return;
    _pendingPrimaryUrl = newUrl;
    final provider = ResizeImage.resizeIfNeeded(
      _lastMemCacheWidth,
      null,
      CachedNetworkImageProvider(newUrl, headers: _headersFor(newUrl)),
    );
    try {
      await precacheImage(provider, context);
    } catch (_) {
      if (_pendingPrimaryUrl == newUrl) _pendingPrimaryUrl = null;
      return;
    }
    if (!mounted) return;
    if (_pendingPrimaryUrl != newUrl) return;
    setState(() {
      _displayedUrls = newAll;
      _failedCount = 0;
      _pendingPrimaryUrl = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (widget.httpHeaders == null && widget.resolveHttpHeaders == null) {
      _embyBaseUrl = ref.watch(activeSessionProvider)?.serverUrl;
      _embyHeaders = ref.watch(embyImageHeadersProvider).value;
    } else {
      _embyBaseUrl = null;
      _embyHeaders = null;
    }

    final scheme = Theme.of(context).colorScheme;
    final isLight = scheme.brightness == Brightness.light;

    Widget placeholderWidget() =>
        widget.placeholder ?? _ShimmerPlaceholder(isLight: isLight);

    final urls = _displayedUrls;
    final effectiveUrl = _failedCount < urls.length ? urls[_failedCount] : null;

    final fallback = widget.fallbackIcon != null
        ? Container(
            color: scheme.surfaceContainerHighest,
            child: Center(
              child: Icon(
                widget.fallbackIcon,
                size: 32,
                color: scheme.onSurfaceVariant.withValues(alpha: 0.4),
              ),
            ),
          )
        : const SizedBox.shrink();

    final Widget child;
    if (effectiveUrl == null) {
      child = KeyedSubtree(
        key: const ValueKey<String>('_smart_image_empty'),
        child: fallback,
      );
    } else {
      final effectiveHeaders = _headersFor(effectiveUrl);
      child = LayoutBuilder(
        builder: (context, constraints) {
          final memCacheWidth =
              widget.memCacheWidth ??
              (constraints.maxWidth.isFinite
                  ? (constraints.maxWidth *
                            MediaQuery.devicePixelRatioOf(context))
                        .ceil()
                  : null);
          _lastMemCacheWidth = memCacheWidth;

          return CachedNetworkImage(
            key: ValueKey<String>(effectiveUrl),
            imageUrl: effectiveUrl,
            httpHeaders: effectiveHeaders,
            fit: widget.fit,
            alignment: widget.alignment,
            memCacheWidth: memCacheWidth,
            fadeInDuration: const Duration(milliseconds: 300),
            fadeInCurve: Curves.easeOut,
            placeholder: (_, _) => placeholderWidget(),
            errorWidget: (_, e, st) {
              if (_failedCount + 1 < urls.length) {
                WidgetsBinding.instance.addPostFrameCallback((_) {
                  if (mounted) setState(() => _failedCount++);
                });
                return placeholderWidget();
              }
              WidgetsBinding.instance.addPostFrameCallback((_) {
                widget.onError?.call();
              });
              return fallback;
            },
            imageBuilder: (ctx, imageProvider) {
              widget.onLoad?.call();
              return Image(
                image: imageProvider,
                fit: widget.fit,
                alignment: widget.alignment,
              );
            },
          );
        },
      );
    }

    final switcher = AnimatedSwitcher(
      duration: const Duration(milliseconds: 200),
      switchInCurve: Curves.easeOut,
      switchOutCurve: Curves.easeOut,
      layoutBuilder: (currentChild, previousChildren) {
        return Stack(
          fit: StackFit.passthrough,
          children: [
            ...previousChildren,
            if (currentChild != null) currentChild,
          ],
        );
      },
      child: child,
    );

    final clipped = ClipRRect(
      borderRadius: BorderRadius.circular(widget.borderRadius),
      child: switcher,
    );

    if (widget.aspectRatio != null) {
      return AspectRatio(aspectRatio: widget.aspectRatio!, child: clipped);
    }
    return clipped;
  }
}

class _ShimmerPlaceholder extends StatefulWidget {
  final bool isLight;
  const _ShimmerPlaceholder({required this.isLight});

  @override
  State<_ShimmerPlaceholder> createState() => _ShimmerPlaceholderState();
}

class _ShimmerPlaceholderState extends State<_ShimmerPlaceholder>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1500),
    )..repeat();
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        final base = widget.isLight
            ? const Color(0xFFF3F4F6)
            : const Color(0xFF2A2A2A);
        final highlight = widget.isLight
            ? const Color(0xFFE5E7EB)
            : const Color(0xFF3A3A3A);
        return Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment(-1.0 + 2.0 * _controller.value, 0),
              end: Alignment(-1.0 + 2.0 * _controller.value + 1.0, 0),
              colors: [base, highlight, base],
              stops: const [0.0, 0.5, 1.0],
            ),
          ),
        );
      },
    );
  }
}
