# Released under the MIT License. See LICENSE for details.
#
"""UI related to league rank."""
# pylint: disable=too-many-lines

from __future__ import annotations

import copy
import logging
from typing import TYPE_CHECKING, override

import bauiv1 as bui
from bauiv1lib.utils import scroll_fade_bottom, scroll_fade_top
from bauiv1lib.popup import PopupMenu

if TYPE_CHECKING:
    from typing import Any


class LeagueRankWindow(bui.MainWindow):
    """Window for showing league rank."""

    def __init__(
        self,
        transition: str | None = 'in_right',
        origin_widget: bui.Widget | None = None,
        auxiliary_style: bool = True,
    ):
        # pylint: disable=too-many-statements
        plus = bui.app.plus
        assert plus is not None

        self._uiopenstate = bui.UIOpenState('classicleaguerank')

        bui.set_analytics_screen('League Rank Window')

        self._league_rank_data: dict[str, Any] | None = None

        self._power_ranking_achievements_button: bui.Widget | None = None
        self._up_to_date_bonus_button: bui.Widget | None = None
        self._power_ranking_trophies_button: bui.Widget | None = None
        self._league_title_text: bui.Widget | None = None
        self._league_text: bui.Widget | None = None
        self._league_number_text: bui.Widget | None = None
        self._your_power_ranking_text: bui.Widget | None = None
        self._loading_spinner: bui.Widget | None = None
        self._season_ends_text: bui.Widget | None = None
        self._power_ranking_rank_text: bui.Widget | None = None
        self._to_ranked_text: bui.Widget | None = None
        self._trophy_counts_reset_text: bui.Widget | None = None

        assert bui.app.classic is not None
        uiscale = bui.app.ui_v1.uiscale
        self._width = 1500 if uiscale is bui.UIScale.SMALL else 1120
        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
        self._height = (
            1000
            if uiscale is bui.UIScale.SMALL
            else 710 if uiscale is bui.UIScale.MEDIUM else 800
        )
        self._r = 'coopSelectWindow'
        self._rdict = bui.app.lang.get_resource(self._r)
        self._xoffs = 40

        self._league_url_arg = ''

        self._is_current_season = False
        self._can_do_more_button = True

        # Do some fancy math to fill all available screen area up to the
        # size of our backing container. This lets us fit to the exact
        # screen shape at small ui scale.
        screensize = bui.get_virtual_screen_size()
        scale = (
            1.13
            if uiscale is bui.UIScale.SMALL
            else 0.93 if uiscale is bui.UIScale.MEDIUM else 0.8
        )
        # Calc screen size in our local container space and clamp to a
        # bit smaller than our container size.
        target_width = min(self._width - 130, screensize[0] / scale)
        target_height = min(self._height - 130, screensize[1] / scale)

        # To get top/left coords, go to the center of our window and
        # offset by half the width/height of our target area.
        yoffs = 0.5 * self._height + 0.5 * target_height + 30.0

        self._scroll_width = target_width
        self._scroll_height = target_height - 50
        scroll_bottom = yoffs - 80 - self._scroll_height

        # Go with full-screen scrollable area in small ui.
        if uiscale is bui.UIScale.SMALL:
            self._scroll_height += 53
            scroll_bottom -= 1

        super().__init__(
            root_widget=bui.containerwidget(
                size=(self._width, self._height),
                scale=scale,
                toolbar_visibility='menu_full',
                toolbar_cancel_button_style=(
                    'close' if auxiliary_style else 'back'
                ),
            ),
            transition=transition,
            origin_widget=origin_widget,
            # We're affected by screen size only at small ui-scale.
            refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
        )

        if uiscale is bui.UIScale.SMALL:
            self._back_button = bui.get_special_widget('back_button')
            bui.containerwidget(
                edit=self._root_widget, on_cancel_call=self.main_window_back
            )
        else:
            self._back_button = bui.buttonwidget(
                parent=self._root_widget,
                id=f'{self.main_window_id_prefix}|back',
                position=(75 + x_inset, yoffs - 60),
                size=(60, 55),
                scale=1.2,
                autoselect=True,
                label=bui.charstr(
                    bui.SpecialChar.CLOSE
                    if auxiliary_style
                    else bui.SpecialChar.BACK
                ),
                button_type=None if auxiliary_style else 'backSmall',
                on_activate_call=self.main_window_back,
            )
            bui.containerwidget(
                edit=self._root_widget,
                cancel_button=self._back_button,
                selected_child=self._back_button,
            )

        self._scrollwidget = bui.scrollwidget(
            parent=self._root_widget,
            highlight=False,
            size=(self._scroll_width, self._scroll_height),
            position=(
                self._width * 0.5 - self._scroll_width * 0.5,
                scroll_bottom,
            ),
            center_small_content=True,
            center_small_content_horizontally=True,
            border_opacity=0.4,
        )
        bui.widget(edit=self._scrollwidget, autoselect=True)
        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)

        # With full-screen scrolling, fade content as it approaches
        # toolbars.
        if uiscale is bui.UIScale.SMALL:
            scroll_fade_top(
                self._root_widget,
                self._width * 0.5 - self._scroll_width * 0.5,
                scroll_bottom,
                self._scroll_width,
                self._scroll_height,
                yscale=0.5,
            )
            scroll_fade_bottom(
                self._root_widget,
                self._width * 0.5 - self._scroll_width * 0.5,
                scroll_bottom,
                self._scroll_width,
                self._scroll_height,
            )

        self._title_text = bui.textwidget(
            parent=self._root_widget,
            position=(
                self._width * 0.5,
                yoffs - (55 if uiscale is bui.UIScale.SMALL else 30),
            ),
            size=(0, 0),
            text=bui.Lstr(
                resource='league.leagueRankText',
                fallback_resource='coopSelectWindow.powerRankingText',
            ),
            h_align='center',
            color=bui.app.ui_v1.title_color,
            scale=1.2 if uiscale is bui.UIScale.SMALL else 1.3,
            maxwidth=600,
            v_align='center',
        )

        self._last_power_ranking_query_time: float | None = None
        self._doing_power_ranking_query = False

        self._subcontainer: bui.Widget | None = None
        self._subcontainerwidth = 1024
        self._subcontainerheight = 573

        # For fullscreen scrollable, account for toolbar.
        if uiscale is bui.UIScale.SMALL:
            self._subcontainerheight += 53

        self._power_ranking_score_widgets: list[bui.Widget] = []

        self._season_popup_menu: PopupMenu | None = None
        self._requested_season: str | None = None
        self._season: str | None = None

        # Take note of our account state; we'll refresh later if this
        # changes.
        self._account_state = plus.get_v1_account_state()

        self._refresh()

        # If we've got cached power-ranking data already, display it.
        assert bui.app.classic is not None
        info = bui.app.classic.accounts.get_cached_league_rank_data()
        if info is not None:
            self._update_for_league_rank_data(info)

        self._update_timer = bui.AppTimer(
            1.0, bui.WeakCallStrict(self._update), repeat=True
        )
        self._update(show=info is None)

    @override
    def get_main_window_state(self) -> bui.MainWindowState:
        # Support recreating our window for back/refresh purposes.
        cls = type(self)
        return bui.BasicMainWindowState(
            create_call=lambda transition, origin_widget: cls(
                transition=transition, origin_widget=origin_widget
            )
        )

    @override
    def main_window_should_preserve_selection(self) -> bool:
        return True

    def _on_achievements_press(self) -> None:
        from bauiv1lib.achievements import AchievementsWindow

        # Only allow this for all-time or the current season (we
        # currently don't keep specific achievement data for old
        # seasons).
        if self._season == 'a' or self._is_current_season:
            prab = self._power_ranking_achievements_button
            assert prab is not None
            self.main_window_replace(
                lambda: AchievementsWindow(origin_widget=prab)
            )
        else:
            bui.screenmessage(
                bui.Lstr(
                    resource='achievementsUnavailableForOldSeasonsText',
                    fallback_resource='unavailableText',
                ),
                color=(1, 0, 0),
            )
            bui.getsound('error').play()

    def _on_activity_mult_press(self) -> None:
        from bauiv1lib import confirm

        plus = bui.app.plus
        assert plus is not None

        txt = bui.Lstr(
            resource=(
                'coopSelectWindow.activenessAllTimeInfoText'
                if self._season == 'a'
                else 'coopSelectWindow.activenessInfoText'
            ),
            subs=[
                (
                    '${MAX}',
                    str(
                        plus.get_v1_account_misc_read_val('activenessMax', 1.0)
                    ),
                )
            ],
        )
        confirm.ConfirmWindow(
            txt,
            cancel_button=False,
            width=460,
            height=150,
            origin_widget=self._activity_mult_button,
        )

    def _on_up_to_date_bonus_press(self) -> None:
        from bauiv1lib import confirm

        plus = bui.app.plus
        assert plus is not None

        txt = bui.Lstr(
            resource='league.upToDateBonusDescriptionText',
            subs=[
                (
                    '${PERCENT}',
                    str(
                        plus.get_v1_account_misc_read_val(
                            'proPowerRankingBoost', 10
                        )
                    ),
                ),
            ],
        )
        confirm.ConfirmWindow(
            txt,
            cancel_button=False,
            width=460,
            height=130,
            origin_widget=self._up_to_date_bonus_button,
        )

    def _on_trophies_press(self) -> None:
        from bauiv1lib.trophies import TrophiesWindow

        info = self._league_rank_data
        if info is not None:
            prtb = self._power_ranking_trophies_button
            assert prtb is not None
            TrophiesWindow(
                position=prtb.get_screen_space_center(),
                data=info,
            )
        else:
            bui.getsound('error').play()

    def _on_power_ranking_query_response(
        self, data: dict[str, Any] | None
    ) -> None:
        self._doing_power_ranking_query = False

        # Important: *only* cache this if we requested the current
        # season.
        if data is not None and data.get('s', None) is None:
            assert bui.app.classic is not None
            bui.app.classic.accounts.cache_league_rank_data(data)

        # Always store a copy locally though (even for other seasons).
        self._league_rank_data = copy.deepcopy(data)
        self._update_for_league_rank_data(data)

    def _update(self, show: bool = False) -> None:
        plus = bui.app.plus
        assert plus is not None

        cur_time = bui.apptime()

        # If our account state has changed, refresh our UI.
        account_state = plus.get_v1_account_state()
        if account_state != self._account_state:
            self._account_state = account_state
            self._refresh()

            # And power ranking too.
            if not self._doing_power_ranking_query:
                self._last_power_ranking_query_time = None

        # Send off a new power-ranking query if its been long enough or
        # our requested season has changed or whatnot.
        if not self._doing_power_ranking_query and (
            self._last_power_ranking_query_time is None
            or cur_time - self._last_power_ranking_query_time > 30.0
        ):
            try:
                if show:
                    bui.textwidget(edit=self._league_title_text, text='')
                    bui.textwidget(edit=self._league_text, text='')
                    bui.textwidget(edit=self._league_number_text, text='')
                    bui.textwidget(edit=self._your_power_ranking_text, text='')
                    bui.spinnerwidget(edit=self._loading_spinner, visible=True)
                    bui.textwidget(edit=self._to_ranked_text, text='')
                    bui.textwidget(edit=self._power_ranking_rank_text, text='')
                    bui.textwidget(edit=self._season_ends_text, text='')
                    bui.textwidget(edit=self._trophy_counts_reset_text, text='')
            except Exception:
                logging.exception('Error showing updated rank info.')

            self._last_power_ranking_query_time = cur_time
            self._doing_power_ranking_query = True
            plus.power_ranking_query(
                season=self._requested_season,
                callback=bui.WeakCallPartial(
                    self._on_power_ranking_query_response
                ),
            )

    def _refresh(self) -> None:
        # pylint: disable=too-many-statements

        plus = bui.app.plus
        assert plus is not None

        # (Re)create the sub-container if need be.
        if self._subcontainer is not None:
            self._subcontainer.delete()
        self._subcontainer = bui.containerwidget(
            parent=self._scrollwidget,
            size=(self._subcontainerwidth, self._subcontainerheight),
            background=False,
        )

        w_parent = self._subcontainer
        v = self._subcontainerheight - 20

        v -= 0

        # For fullscreen scrollable, account for toolbar.
        uiscale = bui.app.ui_v1.uiscale
        if uiscale is bui.UIScale.SMALL:
            v -= 52

        h2 = 80
        v2 = v - 60
        worth_color = (0.6, 0.6, 0.65)
        tally_color = (0.5, 0.6, 0.8)
        spc = 43

        h_offs_tally = 150
        tally_maxwidth = 120
        v2 -= 70

        bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + h2 - 60, v2 + 106),
            size=(0, 0),
            flatness=1.0,
            shadow=0.0,
            text=bui.Lstr(resource='coopSelectWindow.pointsText'),
            h_align='left',
            v_align='center',
            scale=0.8,
            color=(1, 1, 1, 0.3),
            maxwidth=200,
        )

        self._power_ranking_achievements_button = bui.buttonwidget(
            parent=w_parent,
            id=f'{self.main_window_id_prefix}|ach',
            position=(self._xoffs + h2 - 60, v2 + 10),
            size=(200, 80),
            icon=bui.gettexture('achievementsIcon'),
            autoselect=True,
            on_activate_call=bui.WeakCallStrict(self._on_achievements_press),
            up_widget=self._back_button,
            left_widget=self._back_button,
            color=(0.5, 0.5, 0.6),
            textcolor=(0.7, 0.7, 0.8),
            label='',
        )

        self._power_ranking_achievement_total_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
            size=(0, 0),
            flatness=1.0,
            shadow=0.0,
            text='-',
            h_align='left',
            v_align='center',
            scale=0.8,
            color=tally_color,
            maxwidth=tally_maxwidth,
        )

        v2 -= 80

        self._power_ranking_trophies_button = bui.buttonwidget(
            parent=w_parent,
            id=f'{self.main_window_id_prefix}|trophies',
            position=(self._xoffs + h2 - 60, v2 + 10),
            size=(200, 80),
            icon=bui.gettexture('medalSilver'),
            autoselect=True,
            on_activate_call=bui.WeakCallStrict(self._on_trophies_press),
            left_widget=self._back_button,
            color=(0.5, 0.5, 0.6),
            textcolor=(0.7, 0.7, 0.8),
            label='',
        )
        self._power_ranking_trophies_total_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
            size=(0, 0),
            flatness=1.0,
            shadow=0.0,
            text='-',
            h_align='left',
            v_align='center',
            scale=0.8,
            color=tally_color,
            maxwidth=tally_maxwidth,
        )

        v2 -= 100

        bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + h2 - 60, v2 + 86),
            size=(0, 0),
            flatness=1.0,
            shadow=0.0,
            text=bui.Lstr(resource='coopSelectWindow.multipliersText'),
            h_align='left',
            v_align='center',
            scale=0.8,
            color=(1, 1, 1, 0.3),
            maxwidth=200,
        )

        self._activity_mult_button: bui.Widget | None
        if plus.get_v1_account_misc_read_val('act', False):
            self._activity_mult_button = bui.buttonwidget(
                parent=w_parent,
                id=f'{self.main_window_id_prefix}|amult',
                position=(self._xoffs + h2 - 60, v2 + 10),
                size=(200, 60),
                icon=bui.gettexture('heart'),
                icon_color=(0.5, 0, 0.5),
                label=bui.Lstr(resource='coopSelectWindow.activityText'),
                autoselect=True,
                on_activate_call=bui.WeakCallStrict(
                    self._on_activity_mult_press
                ),
                left_widget=self._back_button,
                color=(0.5, 0.5, 0.6),
                textcolor=(0.7, 0.7, 0.8),
            )

            self._activity_mult_text = bui.textwidget(
                parent=w_parent,
                position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
                size=(0, 0),
                flatness=1.0,
                shadow=0.0,
                text='-',
                h_align='left',
                v_align='center',
                scale=0.8,
                color=tally_color,
                maxwidth=tally_maxwidth,
            )
            v2 -= 65
        else:
            self._activity_mult_button = None

        self._up_to_date_bonus_button = bui.buttonwidget(
            parent=w_parent,
            id=f'{self.main_window_id_prefix}|uptodatebonus',
            position=(self._xoffs + h2 - 60, v2 + 10),
            size=(200, 60),
            icon=bui.gettexture('logo'),
            icon_color=(0.3, 0, 0.3),
            label=bui.Lstr(resource='league.upToDateBonusText'),
            autoselect=True,
            on_activate_call=bui.WeakCallStrict(
                self._on_up_to_date_bonus_press
            ),
            left_widget=self._back_button,
            color=(0.5, 0.5, 0.6),
            textcolor=(0.7, 0.7, 0.8),
        )

        self._up_to_date_bonus_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
            size=(0, 0),
            flatness=1.0,
            shadow=0.0,
            text='-',
            h_align='left',
            v_align='center',
            scale=0.8,
            color=tally_color,
            maxwidth=tally_maxwidth,
        )
        v2 -= 30

        v2 -= spc
        bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + h2 + h_offs_tally - 10 - 40, v2 + 35),
            size=(0, 0),
            flatness=1.0,
            shadow=0.0,
            text=bui.Lstr(resource='finalScoreText'),
            h_align='right',
            v_align='center',
            scale=0.9,
            color=worth_color,
            maxwidth=150,
        )
        self._power_ranking_total_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + h2 + h_offs_tally - 40, v2 + 35),
            size=(0, 0),
            flatness=1.0,
            shadow=0.0,
            text='-',
            h_align='left',
            v_align='center',
            scale=0.9,
            color=tally_color,
            maxwidth=tally_maxwidth,
        )

        self._season_show_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 390 - 15, v - 20),
            size=(0, 0),
            color=(0.6, 0.6, 0.7),
            maxwidth=200,
            text=bui.Lstr(resource='showText'),
            h_align='right',
            v_align='center',
            scale=0.8,
            shadow=0,
            flatness=1.0,
        )

        self._league_title_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 470, v - 97),
            size=(0, 0),
            color=(0.6, 0.6, 0.7),
            maxwidth=230,
            text='',
            h_align='center',
            v_align='center',
            scale=0.9,
            shadow=0,
            flatness=1.0,
        )

        self._league_text_scale = 1.8
        self._league_text_maxwidth = 210
        self._league_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 470, v - 140),
            size=(0, 0),
            color=(1, 1, 1),
            maxwidth=self._league_text_maxwidth,
            text='-',
            h_align='center',
            v_align='center',
            scale=self._league_text_scale,
            shadow=1.0,
            flatness=1.0,
        )
        self._league_number_base_pos = (470, v - 140)
        self._league_number_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 470, v - 140),
            size=(0, 0),
            color=(1, 1, 1),
            maxwidth=100,
            text='',
            h_align='left',
            v_align='center',
            scale=0.8,
            shadow=1.0,
            flatness=1.0,
        )

        self._loading_spinner = bui.spinnerwidget(
            parent=w_parent,
            position=(
                self._subcontainerwidth * 0.5,
                self._subcontainerheight * 0.5,
            ),
            style='bomb',
            size=64,
        )
        self._your_power_ranking_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 470, v - 142 - 70),
            size=(0, 0),
            color=(0.6, 0.6, 0.7),
            maxwidth=230,
            text='',
            h_align='center',
            v_align='center',
            scale=0.9,
            shadow=0,
            flatness=1.0,
        )

        self._to_ranked_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 470, v - 250 - 70),
            size=(0, 0),
            color=(0.6, 0.6, 0.7),
            maxwidth=230,
            text='',
            h_align='center',
            v_align='center',
            scale=0.8,
            shadow=0,
            flatness=1.0,
        )

        self._power_ranking_rank_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 473, v - 210 - 70),
            size=(0, 0),
            big=False,
            text='-',
            h_align='center',
            v_align='center',
            scale=1.0,
        )

        self._season_ends_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 470, v - 380),
            size=(0, 0),
            color=(0.6, 0.6, 0.6),
            maxwidth=230,
            text='',
            h_align='center',
            v_align='center',
            scale=0.9,
            shadow=0,
            flatness=1.0,
        )
        self._trophy_counts_reset_text = bui.textwidget(
            parent=w_parent,
            position=(self._xoffs + 470, v - 410),
            size=(0, 0),
            color=(0.5, 0.5, 0.5),
            maxwidth=230,
            text='Trophy counts will reset next season.',
            h_align='center',
            v_align='center',
            scale=0.8,
            shadow=0,
            flatness=1.0,
        )

        self._power_ranking_score_widgets = []

        self._power_ranking_score_v = v - 56

        h = 707
        v -= 451

        self._see_more_button = bui.buttonwidget(
            parent=w_parent,
            id=f'{self.main_window_id_prefix}|seemore',
            label=self._rdict.seeMoreText,
            position=(self._xoffs + h, v),
            color=(0.5, 0.5, 0.6),
            textcolor=(0.7, 0.7, 0.8),
            size=(230, 60),
            autoselect=True,
            on_activate_call=bui.WeakCallStrict(self._on_more_press),
        )

    def _on_more_press(self) -> None:
        plus = bui.app.plus
        assert plus is not None

        our_login_id = plus.get_v1_account_public_login_id()
        if not self._can_do_more_button or our_login_id is None:
            bui.getsound('error').play()
            bui.screenmessage(
                bui.Lstr(resource='unavailableText'), color=(1, 0, 0)
            )
            return
        if self._season is None:
            season_str = ''
        else:
            season_str = '&season=' + (
                'all_time' if self._season == 'a' else self._season
            )
        if self._league_url_arg != '':
            league_str = '&league=' + self._league_url_arg
        else:
            league_str = ''
        bui.open_url(
            plus.get_legacy_master_server_address()
            + '/highscores?list=powerRankings&v=2'
            + league_str
            + season_str
            + '&player='
            + our_login_id
        )

    def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        if not self._root_widget:
            return
        plus = bui.app.plus
        uiscale = bui.app.ui_v1.uiscale
        assert plus is not None
        assert bui.app.classic is not None
        accounts = bui.app.classic.accounts
        in_top = data is not None and data['rank'] is not None
        eq_text = self._rdict.powerRankingPointsEqualsText
        pts_txt = self._rdict.powerRankingPointsText
        num_text = bui.Lstr(resource='numberText').evaluate()
        do_percent = False
        finished_season_unranked = False
        self._can_do_more_button = True
        extra_text = ''
        if plus.get_v1_account_state() != 'signed_in':
            status_text = (
                '(' + bui.Lstr(resource='notSignedInText').evaluate() + ')'
            )
        elif in_top:
            assert data is not None
            status_text = num_text.replace('${NUMBER}', str(data['rank']))
        elif data is not None:
            try:
                # Handle old seasons where we didn't wind up ranked at
                # the end.
                if not data['scores']:
                    status_text = (
                        self._rdict.powerRankingFinishedSeasonUnrankedText
                    )
                    extra_text = ''
                    finished_season_unranked = True
                    self._can_do_more_button = False
                else:
                    our_points = accounts.get_league_rank_points(data)
                    progress = float(our_points) / max(1, data['scores'][-1][1])
                    status_text = str(int(progress * 100.0)) + '%'
                    extra_text = (
                        '\n'
                        + self._rdict.powerRankingPointsToRankedText.replace(
                            '${CURRENT}', str(our_points)
                        ).replace('${REMAINING}', str(data['scores'][-1][1]))
                    )
                    do_percent = True
            except Exception:
                logging.exception('Error updating power ranking.')
                status_text = self._rdict.powerRankingNotInTopText.replace(
                    '${NUMBER}', str(data['listSize'])
                )
                extra_text = ''
        else:
            status_text = '-'

        self._season = data['s'] if data is not None else None

        v = self._subcontainerheight - 20
        # For fullscreen scrollable, account for toolbar.
        uiscale = bui.app.ui_v1.uiscale
        if uiscale is bui.UIScale.SMALL:
            v -= 52

        popup_was_selected = False
        if self._season_popup_menu is not None:
            btn = self._season_popup_menu.get_button()
            assert self._subcontainer
            if self._subcontainer.get_selected_child() == btn:
                popup_was_selected = True
            btn.delete()
        season_choices = []
        season_choices_display = []
        did_first = False
        self._is_current_season = False
        if data is not None:
            # Build our list of seasons we have available.
            for ssn in data['sl']:
                season_choices.append(ssn)
                if ssn != 'a' and not did_first:
                    season_choices_display.append(
                        bui.Lstr(
                            resource='league.currentSeasonText',
                            subs=[('${NUMBER}', ssn)],
                        )
                    )
                    did_first = True

                    # If we either did not specify a season or specified
                    # the first, we're looking at the current.
                    if self._season in [ssn, None]:
                        self._is_current_season = True
                elif ssn == 'a':
                    season_choices_display.append(
                        bui.Lstr(resource='league.allTimeText')
                    )
                else:
                    season_choices_display.append(
                        bui.Lstr(
                            resource='league.seasonText',
                            subs=[('${NUMBER}', ssn)],
                        )
                    )
            assert self._subcontainer
            self._season_popup_menu = PopupMenu(
                parent=self._subcontainer,
                button_id=f'{self.main_window_id_prefix}|season',
                position=(self._xoffs + 390, v - 45),
                width=150,
                button_size=(200, 50),
                choices=season_choices,
                on_value_change_call=bui.WeakCallPartial(
                    self._on_season_change
                ),
                choices_display=season_choices_display,
                current_choice=self._season,
            )
            if popup_was_selected:
                bui.containerwidget(
                    edit=self._subcontainer,
                    selected_child=self._season_popup_menu.get_button(),
                )
            bui.widget(edit=self._see_more_button, show_buffer_bottom=100)
            bui.widget(
                edit=self._season_popup_menu.get_button(),
                up_widget=self._back_button,
            )
            if uiscale is not bui.UIScale.SMALL:
                bui.widget(
                    edit=self._back_button,
                    down_widget=self._power_ranking_achievements_button,
                    right_widget=self._season_popup_menu.get_button(),
                )

        bui.textwidget(
            edit=self._league_title_text,
            text=(
                ''
                if self._season == 'a'
                else bui.Lstr(resource='league.leagueText')
            ),
        )

        if data is None:
            lname = ''
            lnum = ''
            lcolor = (1, 1, 1)
            self._league_url_arg = ''
        elif self._season == 'a':
            lname = bui.Lstr(resource='league.allTimeText').evaluate()
            lnum = ''
            lcolor = (1, 1, 1)
            self._league_url_arg = ''
        else:
            lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else ''
            lname = bui.Lstr(
                translate=('leagueNames', data['l']['n'])
            ).evaluate()
            lcolor = data['l']['c']
            self._league_url_arg = (
                data['l']['n'] + '_' + str(data['l']['i'])
            ).lower()

        to_end_string: bui.Lstr | str
        if data is None or self._season == 'a' or data['se'] is None:
            to_end_string = ''
            show_season_end = False
        else:
            show_season_end = True
            days_to_end = data['se'][0]
            minutes_to_end = data['se'][1]
            if days_to_end > 0:
                to_end_string = bui.Lstr(
                    resource='league.seasonEndsDaysText',
                    subs=[('${NUMBER}', str(days_to_end))],
                )
            elif days_to_end == 0 and minutes_to_end >= 60:
                to_end_string = bui.Lstr(
                    resource='league.seasonEndsHoursText',
                    subs=[('${NUMBER}', str(minutes_to_end // 60))],
                )
            elif days_to_end == 0 and minutes_to_end >= 0:
                to_end_string = bui.Lstr(
                    resource='league.seasonEndsMinutesText',
                    subs=[('${NUMBER}', str(minutes_to_end))],
                )
            else:
                to_end_string = bui.Lstr(
                    resource='league.seasonEndedDaysAgoText',
                    subs=[('${NUMBER}', str(-(days_to_end + 1)))],
                )

        bui.textwidget(edit=self._season_ends_text, text=to_end_string)
        bui.textwidget(
            edit=self._trophy_counts_reset_text,
            text=(
                bui.Lstr(resource='league.trophyCountsResetText')
                if self._is_current_season and show_season_end
                else ''
            ),
        )

        bui.textwidget(edit=self._league_text, text=lname, color=lcolor)
        l_text_width = min(
            self._league_text_maxwidth,
            bui.get_string_width(lname, suppress_warning=True)
            * self._league_text_scale,
        )
        bui.textwidget(
            edit=self._league_number_text,
            text=lnum,
            color=lcolor,
            position=(
                self._xoffs
                + self._league_number_base_pos[0]
                + l_text_width * 0.5
                + 8,
                self._league_number_base_pos[1] + 10,
            ),
        )
        bui.textwidget(
            edit=self._to_ranked_text,
            text=(
                bui.Lstr(resource='coopSelectWindow.toRankedText').evaluate()
                + ''
                + extra_text
                if do_percent
                else ''
            ),
        )

        bui.textwidget(
            edit=self._your_power_ranking_text,
            text=(
                bui.Lstr(
                    resource='rankText',
                    fallback_resource='coopSelectWindow.yourPowerRankingText',
                )
                if (not do_percent)
                else ''
            ),
        )
        bui.spinnerwidget(edit=self._loading_spinner, visible=False)

        bui.textwidget(
            edit=self._power_ranking_rank_text,
            position=(self._xoffs + 473, v - 70 - (170 if do_percent else 220)),
            text=status_text,
            big=(in_top or do_percent),
            scale=(
                3.0
                if (in_top or do_percent)
                else 0.7 if finished_season_unranked else 1.0
            ),
        )

        if self._activity_mult_button is not None:
            if data is None or data['act'] is None:
                bui.buttonwidget(
                    edit=self._activity_mult_button,
                    textcolor=(0.7, 0.7, 0.8, 0.5),
                    icon_color=(0.5, 0, 0.5, 0.3),
                )
                bui.textwidget(edit=self._activity_mult_text, text='     -')
            else:
                bui.buttonwidget(
                    edit=self._activity_mult_button,
                    textcolor=(0.7, 0.7, 0.8, 1.0),
                    icon_color=(0.5, 0, 0.5, 1.0),
                )
                bui.textwidget(
                    edit=self._activity_mult_text,
                    text=f'x {data['act']:.2f}',
                )

        # This used to be a bonus for 'BombSquad Pro' holders, but since
        # we're transitioning away from that it is now a bonus for
        # everyone running a recent-ish version of the game.

        have_up_to_date_bonus = data is not None
        pro_mult = (
            1.0
            + float(
                plus.get_v1_account_misc_read_val('proPowerRankingBoost', 0.0)
            )
            * 0.01
        )
        bui.textwidget(
            edit=self._up_to_date_bonus_text,
            text=(
                '     -'
                if (data is None or not have_up_to_date_bonus)
                else f'x {pro_mult:.2f}'
            ),
        )
        bui.buttonwidget(
            edit=self._up_to_date_bonus_button,
            textcolor=(0.7, 0.7, 0.8, (1.0 if have_up_to_date_bonus else 0.5)),
            icon_color=(
                (0.5, 0, 0.5) if have_up_to_date_bonus else (0.5, 0, 0.5, 0.2)
            ),
        )
        bui.buttonwidget(
            edit=self._power_ranking_achievements_button,
            label=('' if data is None else str(data['a']) + ' ')
            + bui.Lstr(resource='achievementsText').evaluate(),
        )

        # For the achievement value, use the number they gave us for
        # non-current seasons; otherwise calc our own.
        total_ach_value = 0
        for ach in bui.app.classic.ach.achievements:
            if ach.complete:
                total_ach_value += ach.power_ranking_value
        if self._season != 'a' and not self._is_current_season:
            if data is not None and 'at' in data:
                total_ach_value = data['at']

        bui.textwidget(
            edit=self._power_ranking_achievement_total_text,
            text=(
                '-'
                if data is None
                else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value)))
            ),
        )

        total_trophies_count = accounts.get_league_rank_points(
            data, 'trophyCount'
        )
        total_trophies_value = accounts.get_league_rank_points(data, 'trophies')
        bui.buttonwidget(
            edit=self._power_ranking_trophies_button,
            label=('' if data is None else str(total_trophies_count) + ' ')
            + bui.Lstr(resource='trophiesText').evaluate(),
        )
        bui.textwidget(
            edit=self._power_ranking_trophies_total_text,
            text=(
                '-'
                if data is None
                else (
                    '+ '
                    + pts_txt.replace('${NUMBER}', str(total_trophies_value))
                )
            ),
        )

        bui.textwidget(
            edit=self._power_ranking_total_text,
            text=(
                '-'
                if data is None
                else eq_text.replace(
                    '${NUMBER}', str(accounts.get_league_rank_points(data))
                )
            ),
        )
        for widget in self._power_ranking_score_widgets:
            widget.delete()
        self._power_ranking_score_widgets = []

        scores: list = data['scores'] if data is not None else []
        tally_color = (0.5, 0.6, 0.8)
        w_parent = self._subcontainer
        v2 = self._power_ranking_score_v

        for score in scores:
            h2 = 680
            is_us = score[3]
            self._power_ranking_score_widgets.append(
                bui.textwidget(
                    parent=w_parent,
                    position=(self._xoffs + h2 - 20, v2),
                    size=(0, 0),
                    color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7),
                    maxwidth=40,
                    flatness=1.0,
                    shadow=0.0,
                    text=num_text.replace('${NUMBER}', str(score[0])),
                    h_align='right',
                    v_align='center',
                    scale=0.5,
                )
            )
            self._power_ranking_score_widgets.append(
                bui.textwidget(
                    parent=w_parent,
                    position=(self._xoffs + h2 + 20, v2),
                    size=(0, 0),
                    color=(1, 1, 1) if is_us else tally_color,
                    maxwidth=60,
                    text=str(score[1]),
                    flatness=1.0,
                    shadow=0.0,
                    h_align='center',
                    v_align='center',
                    scale=0.7,
                )
            )
            txt = bui.textwidget(
                parent=w_parent,
                position=(self._xoffs + h2 + 60, v2 - (28 * 0.5) / 0.9),
                size=(210 / 0.9, 28),
                color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6),
                maxwidth=210,
                flatness=1.0,
                shadow=0.0,
                autoselect=True,
                selectable=True,
                click_activate=True,
                text=score[2],
                h_align='left',
                v_align='center',
                scale=0.9,
            )
            # Giving these ids doesn't work cleanly with sel
            # save/restore due to refreshing, so let's just not for now.
            bui.widget(edit=txt, allow_preserve_selection=False)
            self._power_ranking_score_widgets.append(txt)
            bui.textwidget(
                edit=txt,
                on_activate_call=bui.CallStrict(
                    self._show_account_info, score[4], txt
                ),
            )
            assert self._season_popup_menu is not None
            bui.widget(
                edit=txt, left_widget=self._season_popup_menu.get_button()
            )
            v2 -= 28

    def _show_account_info(
        self, account_id: str, textwidget: bui.Widget
    ) -> None:
        from bauiv1lib.account.viewer import AccountViewerWindow

        bui.getsound('swish').play()
        AccountViewerWindow(
            account_id=account_id, position=textwidget.get_screen_space_center()
        )

    def _on_season_change(self, value: str) -> None:
        self._requested_season = value
        self._last_power_ranking_query_time = None  # Update asap.
        self._update(show=True)
