"""Collection of `Control`s."""

import time
from argparse import ArgumentParser

from libcurses import register_fkey
from loguru import logger

# from tf2mon.command import CommandManager
from tf2mon.control import Control
from tf2mon.fkey import FKey
from tf2mon.pkg import APPNAME, APPTAG


class Controller:
    """Collection of `Control`s."""

    # all controls; irrelevant order.
    controls: list[Control] = []

    # subset bound to function-keys; ordered for rendering `--help`.
    bindings: list[Control] = []

    def __init__(self, controls: list[Control] | None = None) -> None:
        """Initialize `Controller` with list of `Control`s."""

        if controls is not None:
            self.controls = controls

    def bind(self, control: Control, keyspec: str) -> None:
        """Bind `control` to `keyspec`."""

        control.fkey = FKey(keyspec)
        control.fkey.bind(control)
        self.bindings.append(control)

    def add_arguments_to(self, parser: ArgumentParser) -> None:
        """Add arguments for all controls to `parser`."""

        Control.cli = parser.get_default("cli")

        for control in [x for x in self.controls if hasattr(x, "add_arguments_to")]:
            control.add_arguments_to(parser)

    def fkey_help(self) -> str:
        """Return help for function keys."""

        lines = []
        for control in self.bindings:
            # 17 == indent 4 + len("KP_RIGHTARROW")
            assert control.fkey
            lines.append(f"{control.fkey.longname:>17} {control.__doc__}")
        return "\n".join(lines)

    def start(self) -> None:
        """Finalize initialization now that curses has been started."""

        for control in self.controls:
            if hasattr(control, "start"):
                logger.debug(f"Starting {control.__class__.__name__}")
                control.start()
            # else:
            #     logger.warning(f"Not starting {control.__class__.__name__}")

        for control in [x for x in self.bindings if x.fkey and not x.fkey.is_ascii]:
            if hasattr(control, "handler"):
                assert control.fkey
                logger.debug(
                    f"Registering {control.__class__.__name__} to {control.fkey.longname}"
                )
                register_fkey(control.handler, control.fkey.key)

    def as_tf2_exec_script(self, static: str, dynamic: str) -> str:
        """Return game key bindings as TF2 exec script."""

        lines = [
            f"// Auto-generated by {APPNAME} on {time.asctime()}",
            "",
            'echo "-------------------------------------------------------------------------"',
            f'echo "Running {static}"',
            "",
            "// Push from game to monitor.",
            'alias tf2mon_push               "status; tf_lobby_debug"',
            "",
            "// Pull from monitor into game.",
            f'alias tf2mon_pull               "exec {dynamic}"',
            "",
            "// which constantly redefines these aliases:",
            'alias tf2mon_kicks_pop          "tf2mon_pull; _tf2mon_kicks_pop"',
            'alias tf2mon_kicks_popleft      "tf2mon_pull; _tf2mon_kicks_popleft"',
            'alias tf2mon_spams_pop          "tf2mon_pull; _tf2mon_spams_pop"',
            'alias tf2mon_spams_popleft      "tf2mon_pull; _tf2mon_spams_popleft"',
            "",
            f'alias tf2mon_kicks_clear        "echo {APPTAG}KICKS-CLEAR"',
            f'alias tf2mon_spams_clear        "echo {APPTAG}SPAMS-CLEAR"',
            f'alias tf2mon_clear_queues       "echo {APPTAG}CLEAR-QUEUES"',
            "",
            "// Bind unmodified keys directly to their `base` commands.",
        ]

        for key in [
            x for x in FKey.pkeys.values() if x.name and x.base and not (x.ctrl or x.shift)
        ]:
            if key.base and hasattr(key.base, "action") and key.base.action:
                lines.append(f'bind "{key.name}" "{key.base.action}"')

        lines.extend(
            [
                "",
                "// Bind modified keys indirectly through aliases.",
            ]
        )

        _base = ["_user_bind_base", "_class_bind_base"]
        _ctrl = ["_user_bind_ctrl", "_class_bind_ctrl"]
        _shift = ["_user_bind_shift", "_class_bind_shift"]

        for key in [x for x in FKey.pkeys.values() if x.name and (x.ctrl or x.shift)]:
            if key.base and hasattr(key.base, "action") and key.base.action:
                alias = f"_b{key.name}"
                lines.append(f'alias {alias} "{key.base.action}"')
                _base.append(f"bind {key.name} {alias}")

            if key.ctrl and hasattr(key.ctrl, "action") and key.ctrl.action:
                alias = f"_c{key.name}"
                lines.append(f'alias {alias} "{key.ctrl.action}"')
                _ctrl.append(f"bind {key.name} {alias}")

            if key.shift and hasattr(key.shift, "action") and key.shift.action:
                alias = f"_s{key.name}"
                lines.append(f'alias {alias} "{key.shift.action}"')
                _shift.append(f"bind {key.name} {alias}")

        lines.extend(
            [
                "",
                "// User class scripts may hook in through these;",
                "// e.g., `.tf2/cfg/user/engineer.cfg` could:",
                '//   alias _class_bind_base "bind 1 slot1; bind 2 slot2" etc.',
                '//   alias _class_bind_shift "bind 1 build_sentry; bind 2 build_dispenser" etc',
                'alias _class_bind_base ""',
                'alias _class_bind_ctrl ""',
                'alias _class_bind_shift ""',
                "",
                "// User scripts may hook in through these.",
                "// e.g., `.tf2/cfg/user/bad-dad-aliases.cfg` could:",
                '//   alias _user_bind_base "bind a +moveleft"',
                '//   alias _user_bind_shift "bind a load_itempreset 0"',
                'alias _user_bind_base ""',
                'alias _user_bind_ctrl ""',
                'alias _user_bind_shift ""',
                "",
                "// Toggle modified keys.",
            ]
        )

        # alias _bind_base "bind f1 _bf1; bind f2 _bf2; bind f3 _bf3; bind f4 _bf4"
        # alias _bind_ctrl "bind f1 _cf1; bind f2 _cf2; bind f3 _cf3; bind f4 _cf4"
        # alias _bind_shift "bind f1 _sf1; bind f2 _sf2; bind f3 _sf3; bind f4 _sf4"

        if binds := "; ".join(_base):
            lines.append(f'alias _bind_base "{binds}"')

        if binds := "; ".join(_ctrl):
            lines.append(f'alias _bind_ctrl "{binds}"')

        if binds := "; ".join(_shift):
            lines.append(f'alias _bind_shift "{binds}"')

        lines.extend(
            [
                "",
                'bind ctrl "+ctrled"',
                "alias +ctrled _bind_ctrl",
                "alias -ctrled _base_base",
                "",
                'bind shift "+shifted"',
                "alias +shifted _bind_shift",
                "alias -shifted _bind_base",
                "",
                "_bind_base",
            ]
        )

        return "\n".join(lines) + "\n"

    def get_status_line(self) -> str:
        """Return 1-line string showing current state of function keys."""

        items: list[str] = []
        for pkey in FKey.pkeys.values():
            for control in pkey.bindings:
                assert control is not None
                if hasattr(control, "status") and control.status:
                    items.append(f"{control.fkey.shortname}={control.status()}")
        return " ".join(items)
