# Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later

import asyncio
import os

from django.core.management import BaseCommand
from django.utils.http import urlencode
from django.utils.translation import activate
from participation.models import Team, Tournament
from registration.models import Registration, VolunteerRegistration
from tfjm.matrix import Matrix, RoomPreset, RoomVisibility


class Command(BaseCommand):
    def handle(self, *args, **options):  # noqa: C901
        activate("fr")

        async def main():
            await Matrix.set_display_name("Bot du TFJM²")

            if not os.getenv("SYNAPSE_PASSWORD"):
                avatar_uri = "plop"
            else:  # pragma: no cover
                if not os.path.isfile(".matrix_avatar"):
                    avatar_uri = await Matrix.get_avatar()
                    if isinstance(avatar_uri, str):
                        with open(".matrix_avatar", "w") as f:
                            f.write(avatar_uri)
                    else:
                        stat_file = os.stat("tfjm/static/logo.png")
                        with open("tfjm/static/logo.png", "rb") as f:
                            resp = (await Matrix.upload(f, filename="../../../tfjm/static/logo.png", content_type="image/png",
                                                        filesize=stat_file.st_size))[0][0]
                        avatar_uri = resp.content_uri
                        with open(".matrix_avatar", "w") as f:
                            f.write(avatar_uri)
                        await Matrix.set_avatar(avatar_uri)

                with open(".matrix_avatar", "r") as f:
                    avatar_uri = f.read().rstrip(" \t\r\n")

            # Create basic channels
            if not await Matrix.resolve_room_alias("#aide-jurys-orgas:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="aide-jurys-orgas",
                    name="Aide jurys & orgas",
                    topic="Pour discuter de propblèmes d'organisation",
                    federate=False,
                    preset=RoomPreset.private_chat,
                )

            if not await Matrix.resolve_room_alias("#annonces:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="annonces",
                    name="Annonces",
                    topic="Informations importantes du TFJM²",
                    federate=False,
                    preset=RoomPreset.public_chat,
                )

            if not await Matrix.resolve_room_alias("#bienvenue:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="bienvenue",
                    name="Bienvenue",
                    topic="Bienvenue au TFJM² 2023 !",
                    federate=False,
                    preset=RoomPreset.public_chat,
                )

            if not await Matrix.resolve_room_alias("#bot:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="bot",
                    name="Bot",
                    topic="Vive les r0b0ts",
                    federate=False,
                    preset=RoomPreset.public_chat,
                )

            if not await Matrix.resolve_room_alias("#cno:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="cno",
                    name="CNO",
                    topic="Channel des dieux",
                    federate=False,
                    preset=RoomPreset.private_chat,
                )

            if not await Matrix.resolve_room_alias("#dev-bot:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="dev-bot",
                    name="Bot - développement",
                    topic="Vive le bot",
                    federate=False,
                    preset=RoomPreset.private_chat,
                )

            if not await Matrix.resolve_room_alias("#faq:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="faq",
                    name="FAQ",
                    topic="Posez toutes vos questions ici !",
                    federate=False,
                    preset=RoomPreset.public_chat,
                )

            if not await Matrix.resolve_room_alias("#flood:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="flood",
                    name="Flood",
                    topic="Discutez de tout et de rien !",
                    federate=False,
                    preset=RoomPreset.public_chat,
                )

            if not await Matrix.resolve_room_alias("#je-cherche-une-equipe:tfjm.org"):
                await Matrix.create_room(
                    visibility=RoomVisibility.public,
                    alias="je-cherche-une-equipe",
                    name="Je cherche une équipe",
                    topic="Le Tinder du TFJM²",
                    federate=False,
                    preset=RoomPreset.public_chat,
                )

            # Setup avatars
            await Matrix.set_room_avatar("#aide-jurys-orgas:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#annonces:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#bienvenue:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#bot:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#cno:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#dev-bot:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#faq:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#flood:tfjm.org", avatar_uri)
            await Matrix.set_room_avatar("#je-cherche-une-equipe:tfjm.org", avatar_uri)

            # Read-only channels
            await Matrix.set_room_power_level_event("#annonces:tfjm.org", "events_default", 50)
            await Matrix.set_room_power_level_event("#bienvenue:tfjm.org", "events_default", 50)

            # Invite everyone to public channels
            for r in Registration.objects.all():
                await Matrix.invite("#annonces:tfjm.org", f"@{r.matrix_username}:tfjm.org")
                await Matrix.invite("#bienvenue:tfjm.org", f"@{r.matrix_username}:tfjm.org")
                await Matrix.invite("#bot:tfjm.org", f"@{r.matrix_username}:tfjm.org")
                await Matrix.invite("#faq:tfjm.org", f"@{r.matrix_username}:tfjm.org")
                await Matrix.invite("#flood:tfjm.org", f"@{r.matrix_username}:tfjm.org")
                await Matrix.invite("#je-cherche-une-equipe:tfjm.org",
                                    f"@{r.matrix_username}:tfjm.org")
                self.stdout.write(f"Invite {r} in most common channels...")

            # Volunteers have access to the help channel
            for volunteer in VolunteerRegistration.objects.all():
                await Matrix.invite("#aide-jurys-orgas:tfjm.org", f"@{volunteer.matrix_username}:tfjm.org")
                self.stdout.write(f"Invite {volunteer} in #aide-jury-orgas...")

            # Admins are admins
            for admin in VolunteerRegistration.objects.filter(admin=True).all():
                self.stdout.write(f"Invite {admin} in #cno and #dev-bot...")
                await Matrix.invite("#cno:tfjm.org", f"@{admin.matrix_username}:tfjm.org")
                await Matrix.invite("#dev-bot:tfjm.org", f"@{admin.matrix_username}:tfjm.org")

                self.stdout.write(f"Give admin permissions for {admin}...")
                await Matrix.set_room_power_level("#aide-jurys-orgas:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#annonces:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#bienvenue:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#bot:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#cno:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#dev-bot:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#faq:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#flood:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)
                await Matrix.set_room_power_level("#je-cherche-une-equipe:tfjm.org",
                                                  f"@{admin.matrix_username}:tfjm.org", 95)

            # Create tournament-specific channels
            for tournament in Tournament.objects.all():
                self.stdout.write(f"Managing tournament of {tournament.name}.")

                name = tournament.name
                slug = name.lower().replace(" ", "-")

                if not await Matrix.resolve_room_alias(f"#annonces-{slug}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"annonces-{slug}",
                        name=f"{name} - Annonces",
                        topic=f"Annonces du tournoi de {name}",
                        federate=False,
                        preset=RoomPreset.private_chat,
                    )

                if not await Matrix.resolve_room_alias(f"#general-{slug}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"general-{slug}",
                        name=f"{name} - Général",
                        topic=f"Accueil du tournoi de {name}",
                        federate=False,
                        preset=RoomPreset.private_chat,
                    )

                if not await Matrix.resolve_room_alias(f"#flood-{slug}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"flood-{slug}",
                        name=f"{name} - Flood",
                        topic=f"Discussion libre du tournoi de {name}",
                        federate=False,
                        preset=RoomPreset.private_chat,
                    )

                if not await Matrix.resolve_room_alias(f"#jury-{slug}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"jury-{slug}",
                        name=f"{name} - Jury",
                        topic=f"Discussion entre les orgas et jurys du tournoi de {name}",
                        federate=False,
                        preset=RoomPreset.private_chat,
                    )

                if not await Matrix.resolve_room_alias(f"#orga-{slug}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"orga-{slug}",
                        name=f"{name} - Organisateurs",
                        topic=f"Discussion entre les orgas du tournoi de {name}",
                        federate=False,
                        preset=RoomPreset.private_chat,
                    )

                if not await Matrix.resolve_room_alias(f"#tirage-au-sort-{slug}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"tirage-au-sort-{slug}",
                        name=f"{name} - Tirage au sort",
                        topic=f"Tirage au sort du tournoi de {name}",
                        federate=False,
                        preset=RoomPreset.private_chat,
                    )

                # Setup avatars
                await Matrix.set_room_avatar(f"#annonces-{slug}:tfjm.org", avatar_uri)
                await Matrix.set_room_avatar(f"#flood-{slug}:tfjm.org", avatar_uri)
                await Matrix.set_room_avatar(f"#general-{slug}:tfjm.org", avatar_uri)
                await Matrix.set_room_avatar(f"#jury-{slug}:tfjm.org", avatar_uri)
                await Matrix.set_room_avatar(f"#orga-{slug}:tfjm.org", avatar_uri)
                await Matrix.set_room_avatar(f"#tirage-au-sort-{slug}:tfjm.org", avatar_uri)

                # Invite admins and give permissions
                for admin in VolunteerRegistration.objects.filter(admin=True).all():
                    self.stdout.write(f"Invite {admin} in all channels of the tournament {name}...")
                    await Matrix.invite(f"#annonces-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#flood-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#general-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#jury-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#orga-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org", f"@{admin.matrix_username}:tfjm.org")

                    self.stdout.write(f"Give permissions to {admin} in all channels of the tournament {name}...")
                    await Matrix.set_room_power_level(f"#annonces-{slug}:tfjm.org",
                                                      f"@{admin.matrix_username}:tfjm.org", 95)
                    await Matrix.set_room_power_level(f"#flood-{slug}:tfjm.org",
                                                      f"@{admin.matrix_username}:tfjm.org", 95)
                    await Matrix.set_room_power_level(f"#general-{slug}:tfjm.org",
                                                      f"@{admin.matrix_username}:tfjm.org", 95)
                    await Matrix.set_room_power_level(f"#jury-{slug}:tfjm.org",
                                                      f"@{admin.matrix_username}:tfjm.org", 95)
                    await Matrix.set_room_power_level(f"#orga-{slug}:tfjm.org",
                                                      f"@{admin.matrix_username}:tfjm.org", 95)
                    await Matrix.set_room_power_level(f"#tirage-au-sort-{slug}:tfjm.org",
                                                      f"@{admin.matrix_username}:tfjm.org", 95)

                # Invite organizers and give permissions
                for orga in tournament.organizers.all():
                    self.stdout.write(f"Invite organizer {orga} in all channels of the tournament {name}...")
                    await Matrix.invite(f"#annonces-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#flood-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#general-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#jury-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#orga-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org")
                    await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org", f"@{orga.matrix_username}:tfjm.org")

                    if not orga.is_admin:
                        await Matrix.set_room_power_level(f"#annonces-{slug}:tfjm.org",
                                                          f"@{orga.matrix_username}:tfjm.org", 50)
                        await Matrix.set_room_power_level(f"#flood-{slug}:tfjm.org",
                                                          f"@{orga.matrix_username}:tfjm.org", 50)
                        await Matrix.set_room_power_level(f"#general-{slug}:tfjm.org",
                                                          f"@{orga.matrix_username}:tfjm.org", 50)
                        await Matrix.set_room_power_level(f"#jury-{slug}:tfjm.org",
                                                          f"@{orga.matrix_username}:tfjm.org", 50)
                        await Matrix.set_room_power_level(f"#orga-{slug}:tfjm.org",
                                                          f"@{orga.matrix_username}:tfjm.org", 50)
                        await Matrix.set_room_power_level(f"#tirage-au-sort-{slug}:tfjm.org",
                                                          f"@{orga.matrix_username}:tfjm.org", 50)

                # Invite participants
                for participation in tournament.participations.filter(valid=True).all():
                    for participant in participation.team.participants.all():
                        self.stdout.write(f"Invite {participant} in public channels of the tournament {name}...")
                        await Matrix.invite(f"#annonces-{slug}:tfjm.org",
                                            f"@{participant.matrix_username}:tfjm.org")
                        await Matrix.invite(f"#flood-{slug}:tfjm.org",
                                            f"@{participant.matrix_username}:tfjm.org")
                        await Matrix.invite(f"#general-{slug}:tfjm.org",
                                            f"@{participant.matrix_username}:tfjm.org")
                        await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org",
                                            f"@{participant.matrix_username}:tfjm.org")

                # Create pool-specific channels
                for pool in tournament.pools.all():
                    self.stdout.write(f"Managing {pool}...")
                    five = pool.participations.count() >= 5
                    for i in range(2 if five else 1):
                        # Fix for five teams-pools
                        suffix = f"-{chr(ord('A') + i)}" if five else ""
                        if not await Matrix.resolve_room_alias(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org"):
                            await Matrix.create_room(
                                visibility=RoomVisibility.public,
                                alias=f"poule-{slug}-{pool.id}{suffix}",
                                name=f"{name} - Jour {pool.round} - Poule " +
                                     ', '.join(participation.team.trigram
                                               for participation in pool.participations.all()) + suffix,
                                topic=f"Discussion avec les équipes - {pool}{suffix}",
                                federate=False,
                                preset=RoomPreset.private_chat,
                            )
                        if not await Matrix.resolve_room_alias(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org"):
                            await Matrix.create_room(
                                visibility=RoomVisibility.public,
                                alias=f"poule-{slug}-{pool.id}{suffix}-jurys",
                                name=f"{name} - Jour {pool.round}{suffix} - Jurys poule " +
                                     ', '.join(participation.team.trigram
                                               for participation in pool.participations.all()) + suffix,
                                topic=f"Discussion avec les jurys - {pool}{suffix}",
                                federate=False,
                                preset=RoomPreset.private_chat,
                            )

                        await Matrix.set_room_avatar(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org", avatar_uri)
                        await Matrix.set_room_avatar(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org", avatar_uri)

                        bbb_url = pool.bbb_url.strip()
                        if five and ';' in bbb_url:
                            bbb_url = bbb_url.split(";")[i].strip()
                        url_params = urlencode(dict(url=bbb_url,
                                                    isAudioConf='false', displayName='$matrix_display_name',
                                                    avatarUrl='$matrix_avatar_url', userId='$matrix_user_id')) \
                            .replace("%24", "$")
                        await Matrix.add_integration(
                            f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                            f"https://scalar.vector.im/api/widgets/bigbluebutton.html?{url_params}",
                            f"bbb-{slug}-{pool.id}{suffix}", "bigbluebutton", "BigBlueButton", str(pool))
                        await Matrix.add_integration(
                            f"#poule-{slug}-{pool.id}:tfjm.org",
                            f"https://board.tfjm.org/boards/{slug}-{pool.id}", f"board-{slug}-{pool.id}",
                            "customwidget", "Tableau", str(pool))

                        # Invite admins and give permissions
                        for admin in VolunteerRegistration.objects.filter(admin=True).all():
                            await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                                                f"@{admin.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org",
                                                f"@{admin.matrix_username}:tfjm.org")

                            await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                                                              f"@{admin.matrix_username}:tfjm.org", 95)
                            await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org",
                                                              f"@{admin.matrix_username}:tfjm.org", 95)

                        # Invite organizers and give permissions
                        for orga in tournament.organizers.all():
                            await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                                                f"@{orga.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org",
                                                f"@{orga.matrix_username}:tfjm.org")

                            if not orga.is_admin:
                                await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                                                                  f"@{orga.matrix_username}:tfjm.org", 50)
                                await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org",
                                                                  f"@{orga.matrix_username}:tfjm.org", 50)

                        # Invite the jury, give good permissions
                        for jury in pool.juries.all():
                            await Matrix.invite(f"#annonces-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#general-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#flood-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#jury-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#orga-{slug}:tfjm.org", f"@{jury.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                                                f"@{jury.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org",
                                                f"@{jury.matrix_username}:tfjm.org")
                            await Matrix.invite(f"#tirage-au-sort-{slug}:tfjm.org",
                                                f"@{jury.matrix_username}:tfjm.org")

                            if not jury.is_admin:
                                await Matrix.set_room_power_level(f"#jury-{slug}:tfjm.org",
                                                                  f"@{jury.matrix_username}:tfjm.org", 50)
                                await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                                                                  f"@{jury.matrix_username}:tfjm.org", 50)
                                await Matrix.set_room_power_level(f"#poule-{slug}-{pool.id}{suffix}-jurys:tfjm.org",
                                                                  f"@{jury.matrix_username}:tfjm.org", 50)

                        # Invite participants to the right pool
                        for participation in pool.participations.all():
                            for participant in participation.team.participants.all():
                                await Matrix.invite(f"#poule-{slug}-{pool.id}{suffix}:tfjm.org",
                                                    f"@{participant.matrix_username}:tfjm.org")

            # Create private channels for teams
            for team in Team.objects.all():
                self.stdout.write(f"Create private channel for {team}...")
                if not await Matrix.resolve_room_alias(f"#equipe-{team.trigram.lower()}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"equipe-{team.trigram.lower()}",
                        name=f"Équipe {team.trigram}",
                        topic=f"Discussion interne de l'équipe {team.name}",
                        federate=False,
                        preset=RoomPreset.private_chat,
                    )
                for participant in team.participants.all():
                    await Matrix.invite(f"#equipe-{team.trigram.lower}:tfjm.org",
                                        f"@{participant.matrix_username}:tfjm.org")
                    await Matrix.set_room_power_level(f"#equipe-{team.trigram.lower()}:tfjm.org",
                                                      f"@{participant.matrix_username}:tfjm.org", 50)

            """
            # Manage channels to discuss about problems
            for i in range(9):
                self.stdout.write(f"Create channel for problem {i}...")
                if not await Matrix.resolve_room_alias(f"#mec-{i}:tfjm.org"):
                    await Matrix.create_room(
                        visibility=RoomVisibility.public,
                        alias=f"mec-{i}",
                        name=f"Mise en commun - {'Général' if i == 0 else f'Problème {i}'}",
                        topic=f"Discussion autour des problèmes",
                        federate=False,
                        preset=RoomPreset.public_chat,
                        invite=[f"@{registration.matrix_username}:tfjm.org"
                                for registration in Registration.objects.all()],
                        power_level_override={
                            f"@{registration.matrix_username}:tfjm.org": (95 if registration.is_admin else 50)
                            for registration in VolunteerRegistration.objects.all()
                        },
                    )
                    await Matrix.set_room_avatar(f"#mec-{i}:tfjm.org", avatar_uri)

                for registration in Registration.objects.all():
                    await Matrix.invite(f"#mec-{i}:tfjm.org", registration.matrix_username)

                for registration in VolunteerRegistration.objects.all():
                    await Matrix.set_room_power_level(f"#mec-{i}:tfjm.org",
                                                      f"@{registration.matrix_username}:tfjm.org",
                                                      95 if registration.is_admin else 50)
            """

        asyncio.get_event_loop().run_until_complete(main())
