import 'package:comunic/helpers/groups_helper.dart';
import 'package:comunic/helpers/users_helper.dart';
import 'package:comunic/lists/group_members_list.dart';
import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/group.dart';
import 'package:comunic/models/group_membership.dart';
import 'package:comunic/models/user.dart';
import 'package:comunic/ui/dialogs/multi_choices_dialog.dart';
import 'package:comunic/ui/dialogs/pick_user_dialog.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart';
import 'package:comunic/ui/widgets/async_screen_widget.dart';
import 'package:comunic/ui/widgets/comunic_back_button_widget.dart';
import 'package:comunic/utils/account_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';

/// Group members screen
///
/// @author Pierre HUBERT

class GroupMembersScreen extends StatefulWidget {
  final int groupID;

  const GroupMembersScreen({Key key, this.groupID})
      : assert(groupID != null),
        super(key: key);

  @override
  _GroupMembersScreenState createState() => _GroupMembersScreenState();
}

class _GroupMembersScreenState extends State<GroupMembersScreen> {
  final _key = GlobalKey<AsyncScreenWidgetState>();
  Group _group;
  GroupMembersList _members;
  UsersList _users;

  Future<void> _refresh() async {
    final group = await GroupsHelper().getSingle(widget.groupID, force: true);
    final members = await GroupsHelper.getMembersList(widget.groupID);
    final users = await UsersHelper().getListWithThrow(members.usersID);

    _group = group;
    _members = members;
    _users = users;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: ComunicBackButton(),
        title: Text(tr("Group members")),
      ),
      body: _buildBody(),
    );
  }

  Widget _buildBody() => AsyncScreenWidget(
        key: _key,
        onReload: _refresh,
        onBuild: _buildBodyContent,
        errorMessage: tr("Could not load the list of members of this group!"),
        showOldDataWhileUpdating: true,
      );

  Widget _buildBodyContent() {
    return Stack(
      children: [
        ListView(children: _members.map(_buildGroupMemberTile).toList()),
        _buildInvitationFAB()
      ],
    );
  }

  Widget _buildGroupMemberTile(GroupMembership membership) {
    final user = _users.getUser(membership.userID);

    return _GroupMembershipTile(
      group: _group,
      membership: membership,
      user: user,
      onUpdated: () => _key.currentState.refresh(),
    );
  }

  Widget _buildInvitationFAB() {
    return Positioned(
      right: 20,
      bottom: 20,
      child: FloatingActionButton(
        onPressed: _inviteMember,
        child: Icon(Icons.add),
      ),
    );
  }

  void _inviteMember() async {
    try {
      final userID = await showPickUserDialog(context);
      if (userID == null) return;

      await GroupsHelper.sendInvitation(widget.groupID, userID);
    } catch (e, s) {
      print("Could not invite a user! $e\n$s");
      showSimpleSnack(context, tr("Could not invite a user!"));
    }

    _key.currentState.refresh();
  }
}

List<MultiChoiceEntry<GroupMembershipLevel>> get _membershipLevels => [
      MultiChoiceEntry(
          id: GroupMembershipLevel.ADMINISTRATOR,
          title: tr("Administrator"),
          subtitle: tr("Can change members privileges and group settings")),
      MultiChoiceEntry(
          id: GroupMembershipLevel.MODERATOR,
          title: tr("Moderator"),
          subtitle: tr(
              "Can always create posts, invite users and respond to membership request")),
      MultiChoiceEntry(
          id: GroupMembershipLevel.MEMBER,
          title: tr("Member"),
          subtitle: tr("Can access to all group posts")),
      MultiChoiceEntry(
        id: GroupMembershipLevel.PENDING,
        title: tr("Requested"),
        hidden: true,
      ),
      MultiChoiceEntry(
        id: GroupMembershipLevel.INVITED,
        title: tr("Invited"),
        hidden: true,
      ),
      MultiChoiceEntry(
        id: GroupMembershipLevel.VISITOR,
        title: tr("Visitor"),
        hidden: true,
      ),
    ];

class _GroupMembershipTile extends StatefulWidget {
  final Group group;
  final GroupMembership membership;
  final User user;
  final void Function() onUpdated;

  const _GroupMembershipTile({
    Key key,
    @required this.group,
    @required this.membership,
    @required this.user,
    @required this.onUpdated,
  })  : assert(group != null),
        assert(membership != null),
        assert(user != null),
        assert(onUpdated != null),
        super(key: key);

  @override
  __GroupMembershipTileState createState() => __GroupMembershipTileState();
}

enum _MemberMenuOptions { CHANGE_LEVEL, DELETE }

class __GroupMembershipTileState extends State<_GroupMembershipTile> {
  int get groupID => widget.membership.groupID;

  int get memberID => widget.membership.userID;

  bool get _isCurrentUser => widget.membership.userID == userID();

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: AccountImageWidget(
        user: widget.user,
      ),
      title: Text(widget.user.displayName),
      subtitle: Text(_membershipLevels
          .firstWhere((f) => f.id == widget.membership.level)
          .title),
      trailing: _buildTrailing(),
    );
  }

  Widget _buildTrailing() {
    switch (widget.membership.level) {
      case GroupMembershipLevel.ADMINISTRATOR:
      case GroupMembershipLevel.MODERATOR:
      case GroupMembershipLevel.MEMBER:
        return _buildMemberCase();
        break;

      case GroupMembershipLevel.INVITED:
        return _buildInvitedCase();
        break;

      case GroupMembershipLevel.PENDING:
        return _buildRequestedCase();
        break;

      case GroupMembershipLevel.VISITOR:
        // This case should never happen
        break;
    }

    return null;
  }

  Widget _buildMemberCase() {
    return PopupMenuButton<_MemberMenuOptions>(
      itemBuilder: (c) => [
        // Change membership level
        PopupMenuItem(
          child: Text(tr("Change level")),
          enabled: !_isCurrentUser && widget.group.isAdmin,
          value: _MemberMenuOptions.CHANGE_LEVEL,
        ),

        // Remove membership
        PopupMenuItem(
          child: Text(tr("Remove")),
          value: _MemberMenuOptions.DELETE,
          enabled: !_isCurrentUser,
        ),
      ],
      onSelected: _handleMenu,
    );
  }

  void _handleMenu(_MemberMenuOptions value) {
    switch (value) {
      case _MemberMenuOptions.CHANGE_LEVEL:
        _changeLevel();
        break;

      case _MemberMenuOptions.DELETE:
        _removeMembership();
        break;
    }
  }

  void _changeLevel() async {
    try {
      final newLevel = await showMultiChoicesDialog(
        context: context,
        choices: _membershipLevels,
        defaultChoice: widget.membership.level,
        title: tr("New membership level"),
      );

      if (newLevel == null) return;

      await GroupsHelper.setNewLevel(groupID, memberID, newLevel);
    } catch (e, s) {
      print("Could not change group membership level! $e\n$s");
      showSimpleSnack(context, tr("Could not change group membership level!"));
    }

    widget.onUpdated();
  }

  void _removeMembership() async {
    try {
      if (!await showConfirmDialog(
          context: context,
          message: tr("Do you really want to remove this membership ?")))
        return;

      await GroupsHelper.removeMemberFromGroup(groupID, memberID);
    } catch (e, s) {
      print("Could not remove membership! $e\n$s");
      showSimpleSnack(context, tr("Could not remove this membership!"));
    }

    widget.onUpdated();
  }

  Widget _buildInvitedCase() {
    return MaterialButton(
      onPressed: _cancelMembership,
      child: Text(tr("Cancel").toUpperCase()),
      textColor: Colors.red,
    );
  }

  void _cancelMembership() async {
    try {
      await GroupsHelper.cancelInvitation(groupID, memberID);
    } catch (e, s) {
      print("Could not cancel invitation! $e\n$s");
      showSimpleSnack(context, tr("Could not cancel invitation!"));
    }

    widget.onUpdated();
  }

  Widget _buildRequestedCase() {
    return IntrinsicWidth(
        child: Row(
      children: <Widget>[
        MaterialButton(
          onPressed: () => _respondRequest(false),
          child: Text(tr("Reject").toUpperCase()),
          textColor: Colors.red,
        ),
        MaterialButton(
          onPressed: () => _respondRequest(true),
          child: Text(tr("Accept").toUpperCase()),
          textColor: Colors.green,
        )
      ],
    ));
  }

  void _respondRequest(bool accept) async {
    try {
      await GroupsHelper.respondRequest(groupID, memberID, accept);
    } catch (e, s) {
      print("Could not respond to membership request! $e\n$s");
      showSimpleSnack(context, tr("Could not respond to membership request!"));
    }

    widget.onUpdated();
  }
}