# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to the final screen in multi-teams sessions."""

from typing import override, TYPE_CHECKING, Any, cast

import bascenev1 as bs
from bascenev1 import classicassets

from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity

if TYPE_CHECKING:
    pass


class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
    """Final score screen for a team series."""

    # Dont' play music by default; (we do manually after a delay).
    default_music = None

    def __init__(self, settings: dict):
        super().__init__(settings=settings)
        self._min_view_time = 15.0
        self._is_ffa = isinstance(self.session, bs.FreeForAllSession)
        self._allow_server_transition = True
        self._tips_text = None
        self._default_show_tips = False
        self._ffa_top_player_info: list[Any] | None = None
        self._ffa_top_player_rec: bs.PlayerRecord | None = None

    @override
    def on_begin(self) -> None:
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        from bascenev1lib.actor.text import Text
        from bascenev1lib.actor.image import Image

        bs.set_analytics_screen(
            'FreeForAll Series Victory Screen'
            if self._is_ffa
            else 'Teams Series Victory Screen'
        )
        assert bs.app.classic is not None
        if bs.app.ui_v1.uiscale is bs.UIScale.LARGE:
            sval = classicassets.strings.game.press_any_key_button_play_again
        else:
            sval = classicassets.strings.game.press_any_button_play_again
        self._show_up_next = False
        self._custom_continue_message = sval
        super().on_begin()
        winning_sessionteam = self.settings_raw['winner']

        # Pause a moment before playing victory music.
        bs.timer(0.6, bs.WeakCallStrict(self._play_victory_music))
        bs.timer(
            4.4,
            bs.WeakCallStrict(self._show_winner, self.settings_raw['winner']),
        )
        bs.timer(4.6, self._score_display_sound.play)

        # Score / Name / Player-record.
        player_entries: list[tuple[int, str, bs.PlayerRecord]] = []

        # Note: for ffa, exclude players who haven't entered the game yet.
        if self._is_ffa:
            for _pkey, prec in self.stats.get_records().items():
                if prec.player.in_game:
                    player_entries.append(
                        (
                            prec.player.sessionteam.customdata['score'],
                            prec.getname(full=True),
                            prec,
                        )
                    )
            player_entries.sort(reverse=True, key=lambda x: x[0])
            if len(player_entries) > 0:
                # Store some info for the top ffa player so we can show
                # winner info even if they leave.
                self._ffa_top_player_info = cast(Any, list(player_entries[0]))
                self._ffa_top_player_info[1] = self._ffa_top_player_info[
                    2
                ].getname()
                self._ffa_top_player_info[2] = self._ffa_top_player_info[
                    2
                ].get_icon()
        else:
            for _pkey, prec in self.stats.get_records().items():
                player_entries.append((prec.score, prec.name_full, prec))
            player_entries.sort(reverse=True, key=lambda x: x[0])

        ts_height = 300.0
        ts_h_offs = -390.0
        tval = 6.4
        t_incr = 0.12

        # 'bestOfUseFirstToInstead' was a per-language 0/1 grammar flag;
        # hard-coded to the English value (0) for the strings migration
        # (revisit in Step B; see followups.md).
        always_use_first_to = 0

        session = self.session
        if self._is_ffa:
            assert isinstance(session, bs.FreeForAllSession)
            txt = classicassets.strings.ui.heading_suffix(
                main=classicassets.strings.multiteam.first_to_final(
                    count=str(session.get_ffa_series_length())
                )
            )
        else:
            assert isinstance(session, bs.MultiTeamSession)

            # Some languages may prefer to always show 'first to X' instead of
            # 'best of X'.
            # FIXME: This will affect all clients connected to us even if
            #  they're not using this language. Should try to come up
            #  with a wording that works everywhere.
            if always_use_first_to:
                txt = classicassets.strings.ui.heading_suffix(
                    main=classicassets.strings.multiteam.first_to_final(
                        count=str(session.get_series_length() / 2 + 1)
                    )
                )
            else:
                txt = classicassets.strings.ui.heading_suffix(
                    main=classicassets.strings.multiteam.best_of_final(
                        count=str(session.get_series_length())
                    )
                )

        Text(
            txt,
            v_align=Text.VAlign.CENTER,
            maxwidth=300,
            color=(0.5, 0.5, 0.5, 1.0),
            position=(0, 220),
            scale=1.2,
            transition=Text.Transition.IN_TOP_SLOW,
            h_align=Text.HAlign.CENTER,
            transition_delay=t_incr * 4,
        ).autoretain()

        assert isinstance(session, bs.MultiTeamSession)
        win_score = (session.get_series_length() - 1) // 2 + 1
        lose_score = 0
        for team in self.teams:
            if team.sessionteam.customdata['score'] != win_score:
                lose_score = team.sessionteam.customdata['score']

        if not self._is_ffa:
            Text(
                classicassets.strings.multiteam.games_to(
                    wincount=str(win_score), losecount=str(lose_score)
                ),
                color=(0.5, 0.5, 0.5, 1.0),
                maxwidth=160,
                v_align=Text.VAlign.CENTER,
                position=(0, -215),
                scale=1.8,
                transition=Text.Transition.IN_LEFT,
                h_align=Text.HAlign.CENTER,
                transition_delay=4.8 + t_incr * 4,
            ).autoretain()

        if self._is_ffa:
            v_extra = 120
        else:
            v_extra = 0

        mvp: bs.PlayerRecord | None = None
        mvp_name: str | None = None

        # Show game MVP.
        if not self._is_ffa:
            mvp, mvp_name = None, None
            for entry in player_entries:
                if entry[2].team == winning_sessionteam:
                    mvp = entry[2]
                    mvp_name = entry[1]
                    break
            if mvp is not None:
                Text(
                    classicassets.strings.multiteam.most_valuable_player,
                    color=(0.5, 0.5, 0.5, 1.0),
                    v_align=Text.VAlign.CENTER,
                    maxwidth=300,
                    position=(180, ts_height / 2 + 15),
                    transition=Text.Transition.IN_LEFT,
                    h_align=Text.HAlign.LEFT,
                    transition_delay=tval,
                ).autoretain()
                tval += 4 * t_incr

                Image(
                    mvp.get_icon(),
                    position=(230, ts_height / 2 - 55 + 14 - 5),
                    scale=(70, 70),
                    transition=Image.Transition.IN_LEFT,
                    transition_delay=tval,
                ).autoretain()
                assert mvp_name is not None
                Text(
                    mvp_name,
                    position=(280, ts_height / 2 - 55 + 15 - 5),
                    h_align=Text.HAlign.LEFT,
                    v_align=Text.VAlign.CENTER,
                    maxwidth=170,
                    scale=1.3,
                    color=bs.safecolor(mvp.team.color + (1,)),
                    transition=Text.Transition.IN_LEFT,
                    transition_delay=tval,
                ).autoretain()
                tval += 4 * t_incr

        # Most violent.
        most_kills = 0
        for entry in player_entries:
            if entry[2].kill_count >= most_kills:
                mvp = entry[2]
                mvp_name = entry[1]
                most_kills = entry[2].kill_count
        if mvp is not None:
            Text(
                classicassets.strings.multiteam.most_violent_player,
                color=(0.5, 0.5, 0.5, 1.0),
                v_align=Text.VAlign.CENTER,
                maxwidth=300,
                position=(180, ts_height / 2 - 150 + v_extra + 15),
                transition=Text.Transition.IN_LEFT,
                h_align=Text.HAlign.LEFT,
                transition_delay=tval,
            ).autoretain()
            Text(
                classicassets.strings.ui.parenthesized(
                    note=classicassets.strings.multiteam.kills_tally(
                        count=str(most_kills)
                    )
                ),
                position=(260, ts_height / 2 - 150 - 15 + v_extra),
                color=(0.3, 0.3, 0.3, 1.0),
                scale=0.6,
                h_align=Text.HAlign.LEFT,
                transition=Text.Transition.IN_LEFT,
                transition_delay=tval,
            ).autoretain()
            tval += 4 * t_incr

            Image(
                mvp.get_icon(),
                position=(233, ts_height / 2 - 150 - 30 - 46 + 25 + v_extra),
                scale=(50, 50),
                transition=Image.Transition.IN_LEFT,
                transition_delay=tval,
            ).autoretain()
            assert mvp_name is not None
            Text(
                mvp_name,
                position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15),
                h_align=Text.HAlign.LEFT,
                v_align=Text.VAlign.CENTER,
                maxwidth=180,
                color=bs.safecolor(mvp.team.color + (1,)),
                transition=Text.Transition.IN_LEFT,
                transition_delay=tval,
            ).autoretain()
            tval += 4 * t_incr

        # Most killed.
        most_killed = 0
        mkp, mkp_name = None, None
        for entry in player_entries:
            if entry[2].killed_count >= most_killed:
                mkp = entry[2]
                mkp_name = entry[1]
                most_killed = entry[2].killed_count
        if mkp is not None:
            Text(
                classicassets.strings.multiteam.most_destroyed_player,
                color=(0.5, 0.5, 0.5, 1.0),
                v_align=Text.VAlign.CENTER,
                maxwidth=300,
                position=(180, ts_height / 2 - 300 + v_extra + 15),
                transition=Text.Transition.IN_LEFT,
                h_align=Text.HAlign.LEFT,
                transition_delay=tval,
            ).autoretain()
            Text(
                classicassets.strings.ui.parenthesized(
                    note=classicassets.strings.multiteam.deaths_tally(
                        count=str(most_killed)
                    )
                ),
                position=(260, ts_height / 2 - 300 - 15 + v_extra),
                h_align=Text.HAlign.LEFT,
                scale=0.6,
                color=(0.3, 0.3, 0.3, 1.0),
                transition=Text.Transition.IN_LEFT,
                transition_delay=tval,
            ).autoretain()
            tval += 4 * t_incr
            Image(
                mkp.get_icon(),
                position=(233, ts_height / 2 - 300 - 30 - 46 + 25 + v_extra),
                scale=(50, 50),
                transition=Image.Transition.IN_LEFT,
                transition_delay=tval,
            ).autoretain()
            assert mkp_name is not None
            Text(
                mkp_name,
                position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15),
                h_align=Text.HAlign.LEFT,
                v_align=Text.VAlign.CENTER,
                color=bs.safecolor(mkp.team.color + (1,)),
                maxwidth=180,
                transition=Text.Transition.IN_LEFT,
                transition_delay=tval,
            ).autoretain()
            tval += 4 * t_incr

        # Now show individual scores.
        tdelay = tval
        Text(
            classicassets.strings.game.final_scores,
            color=(0.5, 0.5, 0.5, 1.0),
            position=(ts_h_offs, ts_height / 2),
            transition=Text.Transition.IN_RIGHT,
            transition_delay=tdelay,
        ).autoretain()
        tdelay += 4 * t_incr

        v_offs = 0.0
        tdelay += len(player_entries) * 8 * t_incr
        for _score, name, prec in player_entries:
            tdelay -= 4 * t_incr
            v_offs -= 40
            Text(
                (
                    str(prec.team.customdata['score'])
                    if self._is_ffa
                    else str(prec.score)
                ),
                color=(0.5, 0.5, 0.5, 1.0),
                position=(ts_h_offs + 230, ts_height / 2 + v_offs),
                h_align=Text.HAlign.RIGHT,
                transition=Text.Transition.IN_RIGHT,
                transition_delay=tdelay,
            ).autoretain()
            tdelay -= 4 * t_incr

            Image(
                prec.get_icon(),
                position=(ts_h_offs - 72, ts_height / 2 + v_offs + 15),
                scale=(30, 30),
                transition=Image.Transition.IN_LEFT,
                transition_delay=tdelay,
            ).autoretain()
            Text(
                name,
                position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15),
                h_align=Text.HAlign.LEFT,
                v_align=Text.VAlign.CENTER,
                maxwidth=180,
                color=bs.safecolor(prec.team.color + (1,)),
                transition=Text.Transition.IN_RIGHT,
                transition_delay=tdelay,
            ).autoretain()

        bs.timer(15.0, bs.WeakCallStrict(self._show_tips))

    def _show_tips(self) -> None:
        from bascenev1lib.actor.tipstext import TipsText

        self._tips_text = TipsText(offs_y=70)

    def _play_victory_music(self) -> None:
        # Make sure we don't stomp on the next activity's music choice.
        if not self.is_transitioning_out():
            bs.setmusic(bs.MusicType.VICTORY)

    def _show_winner(self, team: bs.SessionTeam) -> None:
        from bascenev1lib.actor.image import Image
        from bascenev1lib.actor.zoomtext import ZoomText

        if not self._is_ffa:
            offs_v = 0.0
            ZoomText(
                team.name,
                position=(0, 97),
                color=team.color,
                scale=1.15,
                jitter=1.0,
                maxwidth=250,
            ).autoretain()
        else:
            offs_v = -80
            assert isinstance(self.session, bs.MultiTeamSession)
            series_length = self.session.get_ffa_series_length()
            icon: dict | None
            # Pull live player info if they're still around.
            if len(team.players) == 1:
                icon = team.players[0].get_icon()
                player_name = team.players[0].getname(full=True, icon=False)
            # Otherwise use the special info we stored when we came in.
            elif (
                self._ffa_top_player_info is not None
                and self._ffa_top_player_info[0] >= series_length
            ):
                icon = self._ffa_top_player_info[2]
                player_name = self._ffa_top_player_info[1]
            else:
                icon = None
                player_name = 'Player Not Found'

            if icon is not None:
                i = Image(
                    icon,
                    position=(0, 143),
                    scale=(100, 100),
                ).autoretain()
                assert i.node
                bs.animate(i.node, 'opacity', {0.0: 0.0, 0.25: 1.0})

            ZoomText(
                player_name,
                position=(0, 97 + offs_v + (0 if icon is not None else 60)),
                color=team.color,
                scale=1.15,
                jitter=1.0,
                maxwidth=250,
            ).autoretain()

        s_extra = 1.0 if self._is_ffa else 1.0

        # The legacy system had separate player-vs-team wordings
        # here purely because translation coverage differed
        # between them; the opening phrase itself is identical, so
        # the ported entry is shared.
        wins_text = classicassets.strings.multiteam.wins_the_series_intro

        ZoomText(
            wins_text,
            position=(0, -10 + offs_v),
            color=team.color,
            scale=0.65 * s_extra,
            jitter=1.0,
            maxwidth=250,
        ).autoretain()
        ZoomText(
            classicassets.strings.multiteam.series,
            position=(0, -110 + offs_v),
            scale=1.0 * s_extra,
            color=team.color,
            jitter=1.0,
            maxwidth=250,
        ).autoretain()
