import 'dart:async';
import 'dart:math' as math;

import 'package:flutter/material.dart';

enum ToastType { success, error, info }

class _ToastItem {
  const _ToastItem({
    required this.id,
    required this.message,
    required this.type,
  });

  final String id;
  final String message;
  final ToastType type;
}

class ToastService {
  static const Duration _displayDuration = Duration(milliseconds: 3000);
  static final ValueNotifier<List<_ToastItem>> _toasts =
      ValueNotifier<List<_ToastItem>>(<_ToastItem>[]);

  static void success(String message) {
    show(message, type: ToastType.success);
  }

  static void error(String message) {
    show(message, type: ToastType.error);
  }

  static void info(String message) {
    show(message, type: ToastType.info);
  }

  static void show(String message, {ToastType type = ToastType.success}) {
    final trimmed = message.trim();
    if (trimmed.isEmpty) {
      return;
    }

    final item = _ToastItem(
      id: '${DateTime.now().microsecondsSinceEpoch}-${_toasts.value.length}',
      message: trimmed,
      type: type,
    );

    _toasts.value = [..._toasts.value, item];

    unawaited(
      Future<void>.delayed(_displayDuration, () {
        _remove(item.id);
      }),
    );
  }

  static void _remove(String id) {
    final next = _toasts.value.where((toast) => toast.id != id).toList();
    if (next.length == _toasts.value.length) {
      return;
    }
    _toasts.value = next;
  }
}

class ToastViewport extends StatelessWidget {
  const ToastViewport({super.key});

  @override
  Widget build(BuildContext context) {
    return IgnorePointer(
      ignoring: true,
      child: SafeArea(
        child: ValueListenableBuilder<List<_ToastItem>>(
          valueListenable: ToastService._toasts,
          builder: (context, toasts, _) {
            if (toasts.isEmpty) {
              return const SizedBox.shrink();
            }

            final media = MediaQuery.of(context);
            final width = math.min(320.0, media.size.width - 32);

            return Align(
              alignment: Alignment.bottomRight,
              child: Padding(
                padding: const EdgeInsets.only(right: 16, bottom: 16),
                child: SizedBox(
                  width: width > 0 ? width : 320,
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: [
                      for (final toast in toasts)
                        Padding(
                          padding: const EdgeInsets.only(top: 8),
                          child: _ToastCard(item: toast),
                        ),
                    ],
                  ),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

class _ToastCard extends StatefulWidget {
  const _ToastCard({required this.item});

  final _ToastItem item;

  @override
  State<_ToastCard> createState() => _ToastCardState();
}

class _ToastCardState extends State<_ToastCard> {
  bool _visible = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) {
        return;
      }
      setState(() {
        _visible = true;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    final scheme = _toastColorScheme(widget.item.type);
    final icon = _toastIcon(widget.item.type);

    return AnimatedSlide(
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeOutCubic,
      offset: _visible ? Offset.zero : const Offset(1, 0),
      child: AnimatedOpacity(
        duration: const Duration(milliseconds: 220),
        opacity: _visible ? 1 : 0,
        child: DecoratedBox(
          decoration: BoxDecoration(
            color: scheme.background,
            borderRadius: BorderRadius.circular(8),
            border: Border.all(color: scheme.border),
            boxShadow: const [
              BoxShadow(
                color: Color(0x26000000),
                blurRadius: 16,
                offset: Offset(0, 6),
              ),
            ],
          ),
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Icon(icon, size: 20, color: scheme.foreground),
                const SizedBox(width: 12),
                Expanded(
                  child: Text(
                    widget.item.message,
                    style: TextStyle(
                      color: scheme.foreground,
                      fontSize: 14,
                      fontWeight: FontWeight.w400,
                      height: 1.2,
                      decoration: TextDecoration.none,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  _ToastColorScheme _toastColorScheme(ToastType type) {
    switch (type) {
      case ToastType.success:
        return const _ToastColorScheme(
          background: Color(0xFFECFDF5),
          border: Color(0xFFA7F3D0),
          foreground: Color(0xFF065F46),
        );
      case ToastType.error:
        return const _ToastColorScheme(
          background: Color(0xFFFFF1F2),
          border: Color(0xFFFDA4AF),
          foreground: Color(0xFF9F1239),
        );
      case ToastType.info:
        return const _ToastColorScheme(
          background: Color(0xFFEFF6FF),
          border: Color(0xFFBFDBFE),
          foreground: Color(0xFF1E40AF),
        );
    }
  }

  IconData _toastIcon(ToastType type) {
    switch (type) {
      case ToastType.success:
        return Icons.check_circle_outline;
      case ToastType.error:
        return Icons.error_outline;
      case ToastType.info:
        return Icons.info_outline;
    }
  }
}

class _ToastColorScheme {
  const _ToastColorScheme({
    required this.background,
    required this.border,
    required this.foreground,
  });

  final Color background;
  final Color border;
  final Color foreground;
}
