import 'dart:async';

import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';

bool isTyping(GroupMember member) {
  return member.lastTypeIndicator != null &&
      clock
              .now()
              .difference(
                member.lastTypeIndicator!,
              )
              .inSeconds <=
          2;
}

bool hasChatOpen(GroupMember member) {
  return member.lastChatOpened != null &&
      clock
              .now()
              .difference(
                member.lastChatOpened!,
              )
              .inSeconds <=
          3;
}

class TypingIndicator extends StatefulWidget {
  const TypingIndicator({required this.group, super.key});

  final Group group;

  @override
  State<TypingIndicator> createState() => _TypingIndicatorState();
}

class _TypingIndicatorState extends State<TypingIndicator> {
  List<GroupMember> _groupMembers = [];

  StreamSubscription<List<(Contact, GroupMember)>>? membersSub;
  Timer? _periodicUpdate;
  double _wasShownOnce = 0;

  @override
  void initState() {
    super.initState();

    _periodicUpdate = Timer.periodic(const Duration(seconds: 1), (_) {
      filterOpenUsers(_groupMembers);
    });

    final membersStream = twonlyDB.groupsDao.watchGroupMembers(
      widget.group.groupId,
    );
    membersSub = membersStream.listen((update) {
      filterOpenUsers(update.map((m) => m.$2).toList());
    });
  }

  void filterOpenUsers(List<GroupMember> input) {
    if (mounted) {
      setState(() {
        _groupMembers = input.where(hasChatOpen).toList();
      });
    }
  }

  @override
  void dispose() {
    membersSub?.cancel();
    _periodicUpdate?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_groupMembers.isEmpty) {
      return SizedBox(height: _wasShownOnce);
    }

    final height =
        (widget.group.isDirectChat ? 20 : 24) * _groupMembers.length.toDouble();
    _wasShownOnce = height;

    return SizedBox(
      height: height,
      child: Align(
        alignment: Alignment.centerLeft,
        child: Padding(
          padding: const EdgeInsets.only(left: 12),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: _groupMembers
                .map(
                  (member) => Padding(
                    key: Key('typing_indicator_${member.contactId}'),
                    padding: const EdgeInsets.only(right: 8),
                    child: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        if (!widget.group.isDirectChat)
                          Padding(
                            padding: const EdgeInsets.only(right: 6),
                            child: GestureDetector(
                              onTap: () => context.push(
                                Routes.profileContact(member.contactId),
                              ),
                              child: AvatarIcon(
                                contactId: member.contactId,
                                fontSize: 12,
                              ),
                            ),
                          ),
                        Container(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 7,
                            vertical: 6,
                          ),
                          decoration: BoxDecoration(
                            color: getMessageColor(true),
                            borderRadius: BorderRadius.circular(12),
                          ),
                          child: AnimatedTypingDots(
                            isTyping: isTyping(member),
                          ),
                        ),
                      ],
                    ),
                  ),
                )
                .toList(),
          ),
        ),
      ),
    );
  }
}

class AnimatedTypingDots extends StatefulWidget {
  const AnimatedTypingDots({required this.isTyping, super.key});

  final bool isTyping;

  @override
  State<AnimatedTypingDots> createState() => _AnimatedTypingDotsState();
}

class _AnimatedTypingDotsState extends State<AnimatedTypingDots>
    with SingleTickerProviderStateMixin {
  AnimationController? _controller;
  List<Animation<double>>? _animations;

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

    _animations = List.generate(3, (index) {
      final start = index * 0.2;
      final end = start + 0.6;

      return TweenSequence<double>([
        // First half: Animate from 0.5 to 1.0
        TweenSequenceItem(
          tween: Tween<double>(
            begin: 0.5,
            end: 1,
          ).chain(CurveTween(curve: Curves.easeInOut)),
          weight: 50,
        ),
        // Second half: Animate back from 1.0 to 0.5
        TweenSequenceItem(
          tween: Tween<double>(
            begin: 1,
            end: 0.5,
          ).chain(CurveTween(curve: Curves.easeInOut)),
          weight: 50,
        ),
      ]).animate(
        CurvedAnimation(
          parent: _controller!,
          curve: Interval(start, end),
        ),
      );
    });
  }

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

  @override
  Widget build(BuildContext context) {
    if (_animations == null) return const SizedBox.shrink();

    return Row(
      mainAxisSize: MainAxisSize.min,
      children: List.generate(
        3,
        (index) => _AnimatedDot(
          isTyping: widget.isTyping,
          animation: _animations![index],
        ),
      ),
    );
  }
}

class _AnimatedDot extends AnimatedWidget {
  const _AnimatedDot({
    required this.isTyping,
    required Animation<double> animation,
  }) : super(listenable: animation);

  final bool isTyping;

  @override
  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 2),
      child: Opacity(
        opacity: isTyping ? animation.value : 0.5,
        child: Transform.scale(
          scale: isTyping ? 1 + (0.5 * animation.value) : 1,
          child: Container(
            width: 8,
            height: 8,
            decoration: const BoxDecoration(
              color: Colors.white,
              shape: BoxShape.circle,
            ),
          ),
        ),
      ),
    );
  }
}
