import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:surface/types/account.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/widgets/chat/call/call_no_content.dart';
import 'package:surface/widgets/chat/call/call_participant_info.dart';
import 'package:surface/widgets/chat/call/call_participant_menu.dart';
import 'package:surface/widgets/chat/call/call_participant_stats.dart';

abstract class ParticipantWidget extends StatefulWidget {
  static ParticipantWidget widgetFor(ParticipantTrack participantTrack,
      {bool isFixed = false, bool showStatsLayer = false}) {
    if (participantTrack.participant is LocalParticipant) {
      return LocalParticipantWidget(
        participantTrack.participant as LocalParticipant,
        participantTrack.videoTrack,
        isFixed,
        participantTrack.isScreenShare,
        showStatsLayer,
      );
    } else if (participantTrack.participant is RemoteParticipant) {
      return RemoteParticipantWidget(
        participantTrack.participant as RemoteParticipant,
        participantTrack.videoTrack,
        isFixed,
        participantTrack.isScreenShare,
        showStatsLayer,
      );
    }
    throw UnimplementedError('Unknown participant type');
  }

  abstract final Participant participant;
  abstract final VideoTrack? videoTrack;
  abstract final bool isScreenShare;
  abstract final bool isFixed;
  abstract final bool showStatsLayer;
  final VideoQuality quality;

  const ParticipantWidget({
    super.key,
    this.quality = VideoQuality.MEDIUM,
  });
}

class LocalParticipantWidget extends ParticipantWidget {
  @override
  final LocalParticipant participant;
  @override
  final VideoTrack? videoTrack;
  @override
  final bool isFixed;
  @override
  final bool isScreenShare;
  @override
  final bool showStatsLayer;

  const LocalParticipantWidget(
    this.participant,
    this.videoTrack,
    this.isFixed,
    this.isScreenShare,
    this.showStatsLayer, {
    super.key,
  });

  @override
  State<StatefulWidget> createState() => _LocalParticipantWidgetState();
}

class RemoteParticipantWidget extends ParticipantWidget {
  @override
  final RemoteParticipant participant;
  @override
  final VideoTrack? videoTrack;
  @override
  final bool isFixed;
  @override
  final bool isScreenShare;
  @override
  final bool showStatsLayer;

  const RemoteParticipantWidget(
    this.participant,
    this.videoTrack,
    this.isFixed,
    this.isScreenShare,
    this.showStatsLayer, {
    super.key,
  });

  @override
  State<StatefulWidget> createState() => _RemoteParticipantWidgetState();
}

abstract class _ParticipantWidgetState<T extends ParticipantWidget>
    extends State<T> {
  VideoTrack? get _activeVideoTrack;

  TrackPublication? get _firstAudioPublication;

  SnAccount? _userinfoMetadata;

  @override
  void initState() {
    super.initState();
    widget.participant.addListener(onParticipantChanged);
    onParticipantChanged();
  }

  @override
  void dispose() {
    widget.participant.removeListener(onParticipantChanged);
    super.dispose();
  }

  @override
  void didUpdateWidget(covariant T oldWidget) {
    oldWidget.participant.removeListener(onParticipantChanged);
    widget.participant.addListener(onParticipantChanged);
    onParticipantChanged();
    super.didUpdateWidget(oldWidget);
  }

  void onParticipantChanged() {
    setState(() {
      if (widget.participant.metadata != null) {
        _userinfoMetadata = SnAccount.fromJson(
          jsonDecode(widget.participant.metadata!),
        );
      }
    });
  }

  @override
  Widget build(BuildContext ctx) {
    return Stack(
      children: [
        _activeVideoTrack != null && !_activeVideoTrack!.muted
            ? VideoTrackRenderer(
                _activeVideoTrack!,
                fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
              )
            : NoContentWidget(
                userinfo: _userinfoMetadata,
                isFixed: widget.isFixed,
                isSpeaking: widget.participant.isSpeaking,
              ),
        if (widget.showStatsLayer)
          Positioned(
            top: 30,
            right: 30,
            child: ParticipantStatsWidget(participant: widget.participant),
          ),
        Align(
          alignment: Alignment.bottomCenter,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisSize: MainAxisSize.min,
            children: [
              ParticipantInfoWidget(
                title: widget.participant.name.isNotEmpty
                    ? widget.participant.name
                    : widget.participant.identity,
                audioAvailable: _firstAudioPublication?.muted == false &&
                    _firstAudioPublication?.subscribed == true,
                connectionQuality: widget.participant.connectionQuality,
                isScreenShare: widget.isScreenShare,
              ),
            ],
          ),
        ),
      ],
    );
  }
}

class _LocalParticipantWidgetState
    extends _ParticipantWidgetState<LocalParticipantWidget> {
  @override
  LocalTrackPublication<LocalAudioTrack>? get _firstAudioPublication =>
      widget.participant.audioTrackPublications.firstOrNull;

  @override
  VideoTrack? get _activeVideoTrack => widget.videoTrack;
}

class _RemoteParticipantWidgetState
    extends _ParticipantWidgetState<RemoteParticipantWidget> {
  @override
  RemoteTrackPublication<RemoteAudioTrack>? get _firstAudioPublication =>
      widget.participant.audioTrackPublications.firstOrNull;

  @override
  VideoTrack? get _activeVideoTrack => widget.videoTrack;
}

class InteractiveParticipantWidget extends StatelessWidget {
  final double? width;
  final double? height;
  final Color? color;
  final bool isFixedAvatar;
  final ParticipantTrack participant;
  final Function() onTap;

  const InteractiveParticipantWidget({
    super.key,
    this.width,
    this.height,
    this.color,
    this.isFixedAvatar = false,
    required this.participant,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Container(
        width: width,
        height: height,
        color: color,
        child: ParticipantWidget.widgetFor(participant, isFixed: isFixedAvatar),
      ),
      onTap: () => onTap(),
      onLongPress: () {
        if (participant.participant is LocalParticipant) return;
        showModalBottomSheet(
          context: context,
          builder: (context) => ParticipantMenu(
            participant: participant.participant as RemoteParticipant,
            videoTrack: participant.videoTrack,
            isScreenShare: participant.isScreenShare,
          ),
        );
      },
    );
  }
}
