from __future__ import annotations
import uuid
from rich.text import Text
from wcwidth import wcswidth
from textual.widgets.option_list import Option
from textual.screen import Screen
from idlemaxx.models.models import Character, Activity, ActivityCategory, Item, Trade, level_from_xp
from idlemaxx.game import Game
import pendulum
from textual.widgets import OptionList, Label, Input, DataTable, Footer
from textual.app import App, ComposeResult


def _icon_label(icon: str, text: str, col_width: int) -> str:
    """Left-pad `text` so it starts at `col_width + 2` regardless of `icon` display width."""
    pad = col_width - wcswidth(icon)
    return icon + " " * pad + "  " + text


def _back_label(col_width: int) -> Text:
    return Text.assemble(" " * (col_width + 2), ("⬅ ", "red"), "Back")


def _icon_col_width(icons: list[str]) -> int:
    return max(wcswidth(i) for i in icons)


class WrappingOptionList(OptionList):
    def action_cursor_down(self) -> None:
        if self.option_count > 0 and self.highlighted == self.option_count - 1:
            self.highlighted = 0
        else:
            super().action_cursor_down()

    def action_cursor_up(self) -> None:
        if self.option_count > 0 and self.highlighted == 0:
            self.highlighted = self.option_count - 1
        else:
            super().action_cursor_up()


class WrappingDataTable(DataTable):
    def action_cursor_down(self) -> None:
        if self.row_count > 0 and self.cursor_row == self.row_count - 1:
            self.move_cursor(row=0)
        else:
            super().action_cursor_down()

    def action_cursor_up(self) -> None:
        if self.row_count > 0 and self.cursor_row == 0:
            self.move_cursor(row=self.row_count - 1)
        else:
            super().action_cursor_up()


class PopScreen(Screen):
    """Base screen that pops itself on escape."""

    BINDINGS = [("escape", "app.pop_screen", "Back")]


class TableViewScreen(PopScreen):
    """Read-only data table. Escape to go back."""

    def __init__(self, columns: list[str], rows: list[tuple]) -> None:
        super().__init__()
        self._columns = columns
        self._rows = rows

    def compose(self) -> ComposeResult:
        table = DataTable(cursor_type="none", disabled=True)
        table.add_columns(*self._columns)
        for row in self._rows:
            table.add_row(*row)
        yield table
        yield Footer()


class CreateCharacterScreen(Screen[str]):
    BINDINGS = [("escape", "dismiss_cancel", "Back")]

    def action_dismiss_cancel(self) -> None:
        self.dismiss("")

    def compose(self) -> ComposeResult:
        yield Label("Enter character name:")
        yield Input(placeholder="Name", id="character-name")

    def on_input_submitted(self, event: Input.Submitted) -> None:
        self.dismiss(event.value)
        event.stop()


class ConfirmScreen(Screen[bool]):
    BINDINGS = [("escape", "dismiss_cancel", "Back")]

    def action_dismiss_cancel(self) -> None:
        self.dismiss(False)

    def __init__(self, message: str) -> None:
        super().__init__()
        self.message = message

    def compose(self) -> ComposeResult:
        yield Label(self.message)
        yield WrappingOptionList(
            Option("Yes", "yes"),
            Option("No", "no"),
        )

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        self.dismiss(event.option.id == "yes")
        event.stop()


class ActionLimitScreen(Screen["tuple[int | None, bool] | None"]):
    BINDINGS = [("escape", "dismiss_cancel", "Back")]
    app: "IdlemaxxApp"

    def __init__(self, character: Character, activity: Activity) -> None:
        super().__init__()
        self.character = character
        self.activity = activity

    def action_dismiss_cancel(self) -> None:
        self.dismiss(None)

    def compose(self) -> ComposeResult:
        est = self.app.game.estimate_activity(self.character, self.activity)
        default_value = str(est.possible_actions) if est.possible_actions is not None else ""
        cat = self.activity.category
        act_name = self.activity.value.name.replace("_", " ").title()
        yield Label(f"Activity: {cat.icon} {cat.display_name} - {act_name}")
        yield Label("Actions (blank = unlimited):")
        yield Input(value=default_value, placeholder="", id="action-limit-input")
        yield WrappingOptionList(
            Option("▶ Start", "start"),
            Option("+ Add to queue", "queue"),
            Option("⬅ Cancel", "cancel"),
        )

    def _parse_limit(self) -> "int | None | str":
        raw = self.query_one("#action-limit-input", Input).value.strip()
        if not raw:
            return None
        try:
            val = int(raw)
            if val <= 0:
                return "Action count must be a positive integer"
            return val
        except ValueError:
            return "Action count must be a positive integer or blank"

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        if event.option.id == "cancel":
            self.dismiss(None)
            event.stop()
            return
        limit = self._parse_limit()
        if isinstance(limit, str):
            self.app.notify(limit, severity="error")
            event.stop()
            return
        self.dismiss((limit, event.option.id == "queue"))
        event.stop()


class ActivityCategoryScreen(PopScreen):
    app: IdlemaxxApp

    def __init__(self, character: Character) -> None:
        super().__init__()
        self.character = character

    def compose(self) -> ComposeResult:
        yield Label("Choose a category")
        categories = self.app.game.activity_categories()
        icons = [cat.icon for cat in categories] + ["💤"]
        col_width = _icon_col_width(icons)
        yield WrappingOptionList(
            *[Option(_icon_label(cat.icon, cat.display_name, col_width), cat.name) for cat in categories],
            Option(_icon_label("💤", "Idle", col_width), "__idle__"),
            Option(_back_label(col_width), "__back__"),
        )

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        match str(event.option.id):
            case "__idle__":
                rewards = self.app.game.end_activity(self.character)
                if rewards:
                    total_actions = sum(r.actions_completed for r in rewards)
                    self.app.notify(f"Stopped activity ({total_actions} actions completed)", timeout=5)
                self.app.pop_screen()
            case "__back__":
                self.app.pop_screen()
            case category_name:
                self.app.push_screen(ActivityScreen(self.character, ActivityCategory[category_name]))
        event.stop()


class ActivityScreen(PopScreen):
    app: IdlemaxxApp

    def __init__(self, character: Character, category: ActivityCategory) -> None:
        super().__init__()
        self.character = character
        self.category = category

    def compose(self) -> ComposeResult:
        yield Label(f"Choose a {self.category.display_name} activity")
        table = WrappingDataTable(cursor_type="row", id="activity-table")
        table.add_columns("Activity", "Time (s)", "Skill Requirements", "Item Requirements", "XP", "Rewards", "Est.")
        for a in self.app.game.activities_for_category(self.category):
            data = a.value
            skill_reqs = ", ".join(f"{r.skill.value} lv.{r.required_level}" for r in data.skill_requirements) or "-"
            item_reqs = ", ".join(f"{i.item.value}" for i in data.materials) or "-"
            xp = ", ".join(f"{r.skill.value} +{r.xp}" for r in data.skill_rewards) or "-"
            items = ", ".join(f"{r.quantity}x {r.item.value}" for r in data.item_rewards) or "-"
            est = self.app.game.estimate_activity(self.character, a)
            if est.possible_actions is None:
                est_str = "∞"
            else:
                total_items = ", ".join(f"{qty}x {item.value}" for item, qty in est.total_items.items()) or "-"
                total_xp = ", ".join(f"+{xp} {skill.value}" for skill, xp in est.total_xp.items()) or "-"
                est_str = f"{est.possible_actions} actions → {total_items} | {total_xp}"
            table.add_row(data.name, str(data.action_time), skill_reqs, item_reqs, xp, items, est_str, key=data.name)
        table.add_row("Back", "", "", "", "", "", "", key="__back__")
        yield table

    def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
        match str(event.row_key.value):
            case "__back__":
                self.app.pop_screen()  # back to ActivityCategoryScreen
            case activity_name:
                chosen_activity = Activity.from_name(activity_name)

                def on_action_limit(result: "tuple[int | None, bool] | None") -> None:
                    if result is None:
                        return
                    limit, should_queue = result
                    if should_queue:
                        try:
                            self.app.game.queue_activity(self.character, chosen_activity, action_limit=limit)
                        except Exception as e:
                            self.app.notify(str(e), severity="error")
                            return
                        self.app.pop_screen()  # close ActivityScreen
                        self.app.pop_screen()  # close ActivityCategoryScreen
                        self.app.notify(f"Queued {chosen_activity.value.name}!", timeout=5)
                    else:
                        if self.character.current_activity:
                            self.app.game.end_activity(self.character)
                        started = self.app.game.start_activity(self.character, chosen_activity, action_limit=limit)
                        self.app.pop_screen()  # close ActivityScreen
                        self.app.pop_screen()  # close ActivityCategoryScreen
                        self.app.notify(f"{self.character.name} started {started.activity.name}!", timeout=5)

                self.app.push_screen(ActionLimitScreen(self.character, chosen_activity), callback=on_action_limit)
        event.stop()


class QueueScreen(PopScreen):
    app: "IdlemaxxApp"

    def __init__(self, character: Character) -> None:
        super().__init__()
        self.character = character

    def _build_options(self) -> list[Option]:
        options: list[Option] = []
        act = self.character.current_activity
        if act:
            cat = act.activity.category
            name = act.activity.value.name.replace("_", " ").title()
            limit_str = f" ({act.action_limit} actions)" if act.action_limit is not None else ""
            options.append(Option(f"[Active] {cat.icon} {name}{limit_str} — end early", "current"))
        else:
            options.append(Option("💤 Idle", "idle"))
        for i, item in enumerate(self.app.game.get_activity_queue(self.character)):
            cat = item.activity.category
            name = item.activity.value.name.replace("_", " ").title()
            limit_str = f" ({item.action_limit} actions)" if item.action_limit is not None else "∞"
            options.append(Option(f"[#{i + 1}] {cat.icon} {name} — {limit_str} — remove", f"queue_{i}"))
        options.append(Option("⬅ Back", "__back__"))
        return options

    def compose(self) -> ComposeResult:
        yield Label("Queue")
        yield WrappingOptionList(*self._build_options())

    def _refresh(self) -> None:
        ol = self.query_one(WrappingOptionList)
        ol.clear_options()
        for opt in self._build_options():
            ol.add_option(opt)

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        match str(event.option.id):
            case "__back__" | "idle":
                self.app.pop_screen()
            case "current":

                def on_end_confirm(confirmed: bool | None) -> None:
                    if confirmed:
                        self.app.game.end_activity(self.character)
                        self._refresh()

                self.app.push_screen(ConfirmScreen("End current activity early?"), callback=on_end_confirm)
            case queue_key if queue_key.startswith("queue_"):
                idx = int(queue_key.removeprefix("queue_"))
                queue = self.app.game.get_activity_queue(self.character)
                item = queue[idx]

                def on_remove_confirm(confirmed: bool | None) -> None:
                    if confirmed:
                        self.app.game.remove_from_queue(self.character, item)
                        self._refresh()

                self.app.push_screen(ConfirmScreen("Remove from queue?"), callback=on_remove_confirm)
        event.stop()


def _parse_trade_items(s: str) -> "dict[Item, int] | str":
    """Parse 'item_name:qty,...' into a dict. Returns an error string on bad input."""
    if not s.strip():
        return {}
    result: dict[Item, int] = {}
    for part in s.split(","):
        part = part.strip()
        if not part:
            continue
        if ":" not in part:
            return f"Bad format: '{part}' — expected item_name:qty"
        name, _, qty_str = part.partition(":")
        try:
            item = Item[name.strip().upper()]
        except KeyError:
            return f"Unknown item: '{name.strip()}'"
        try:
            qty = int(qty_str.strip())
        except ValueError:
            return f"Bad quantity for '{name.strip()}'"
        result[item] = qty
    return result


class TradeOfferInputScreen(Screen["tuple[dict, dict] | None"]):
    """Enter give/want items. Returns (give, want) dicts or None on cancel."""

    BINDINGS = [("escape", "dismiss_cancel", "Back")]

    def __init__(self, give_prefill: str = "", want_prefill: str = "") -> None:
        super().__init__()
        self._give_prefill = give_prefill
        self._want_prefill = want_prefill

    def action_dismiss_cancel(self) -> None:
        self.dismiss(None)

    def compose(self) -> ComposeResult:
        yield Label("Give (item_name:qty, ...) — blank = nothing")
        yield Input(value=self._give_prefill, placeholder="e.g. regular_log:10", id="give-input")
        yield Label("Want (item_name:qty, ...) — blank = free")
        yield Input(value=self._want_prefill, placeholder="e.g. bronze_bar:5", id="want-input")
        yield WrappingOptionList(
            Option("✓ Send", "send"),
            Option("⬅ Cancel", "cancel"),
        )

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        if event.option.id == "cancel":
            self.dismiss(None)
            event.stop()
            return
        give_str = self.query_one("#give-input", Input).value
        want_str = self.query_one("#want-input", Input).value
        give = _parse_trade_items(give_str)
        if isinstance(give, str):
            self.app.notify(give, severity="error")
            event.stop()
            return
        want = _parse_trade_items(want_str)
        if isinstance(want, str):
            self.app.notify(want, severity="error")
            event.stop()
            return
        self.dismiss((give, want))
        event.stop()


class TradeDetailScreen(PopScreen):
    """View a specific trade and take action: accept, counter, or reject."""

    app: "IdlemaxxApp"

    def __init__(self, trade: Trade, character: Character) -> None:
        super().__init__()
        self.trade = trade
        self.character = character

    def _other_name(self) -> str:
        other_id = (
            self.trade.counterparty_id if self.trade.initiator_id == self.character.id else self.trade.initiator_id
        )
        other = self.app.game._session.get(Character, other_id)
        return other.name if other else "?"

    def _format_sides(self) -> tuple[str, str]:
        offer = self.app.game.get_current_offer(self.trade)
        init_lines = [
            f"{oi.quantity}x {oi.item.value.replace('_', ' ').title()}"
            for oi in offer.items
            if oi.offered_by_id == self.trade.initiator_id
        ]
        cp_lines = [
            f"{oi.quantity}x {oi.item.value.replace('_', ' ').title()}"
            for oi in offer.items
            if oi.offered_by_id == self.trade.counterparty_id
        ]
        return (", ".join(init_lines) or "nothing", ", ".join(cp_lines) or "nothing")

    def compose(self) -> ComposeResult:
        initiator = self.app.game._session.get(Character, self.trade.initiator_id)
        counterparty = self.app.game._session.get(Character, self.trade.counterparty_id)
        initiator_name = initiator.name if initiator else "?"
        counterparty_name = counterparty.name if counterparty else "?"

        init_side, cp_side = self._format_sides()
        n = len(self.trade.offers)
        is_my_turn = self.trade.current_turn_id == self.character.id

        yield Label(f"Trade with {self._other_name()} — {n} revision{'s' if n != 1 else ''}")
        yield Label(f"  {initiator_name} gives: {init_side}")
        yield Label(f"  {counterparty_name} gives: {cp_side}")

        options = []
        if is_my_turn:
            options.append(Option("✓ Accept", "accept"))
            options.append(Option("↩ Counter-offer", "counter"))
        options.append(Option("✗ Reject", "reject"))
        options.append(Option("⬅ Back", "__back__"))
        yield WrappingOptionList(*options)

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        match str(event.option.id):
            case "__back__":
                self.app.pop_screen()
            case "accept":

                def on_confirm(confirmed: bool | None) -> None:
                    if confirmed:
                        try:
                            self.app.game.accept_trade(self.trade, self.character)
                            self.app.pop_screen()
                            self.app.notify("Trade accepted! Items transferred.")
                        except ValueError as e:
                            self.app.notify(str(e), severity="error")

                self.app.push_screen(ConfirmScreen("Accept this trade?"), callback=on_confirm)
            case "counter":
                offer = self.app.game.get_current_offer(self.trade)
                init_items = {oi.item: oi.quantity for oi in offer.items if oi.offered_by_id == self.trade.initiator_id}
                cp_items = {
                    oi.item: oi.quantity for oi in offer.items if oi.offered_by_id == self.trade.counterparty_id
                }

                if self.character.id == self.trade.initiator_id:
                    give_prefill = ",".join(f"{item.value}:{qty}" for item, qty in init_items.items())
                    want_prefill = ",".join(f"{item.value}:{qty}" for item, qty in cp_items.items())
                else:
                    give_prefill = ",".join(f"{item.value}:{qty}" for item, qty in cp_items.items())
                    want_prefill = ",".join(f"{item.value}:{qty}" for item, qty in init_items.items())

                def on_counter_input(result: "tuple[dict, dict] | None") -> None:
                    if result is None:
                        return
                    give, want = result
                    if self.character.id == self.trade.initiator_id:
                        init_new, cp_new = give, want
                    else:
                        init_new, cp_new = want, give
                    try:
                        self.app.game.counter_trade(self.trade, self.character, init_new, cp_new)
                        self.app.pop_screen()
                        self.app.notify("Counter-offer sent!")
                    except ValueError as e:
                        self.app.notify(str(e), severity="error")

                self.app.push_screen(
                    TradeOfferInputScreen(give_prefill=give_prefill, want_prefill=want_prefill),
                    callback=on_counter_input,
                )
            case "reject":

                def on_confirm_reject(confirmed: bool | None) -> None:
                    if confirmed:
                        try:
                            self.app.game.reject_trade(self.trade, self.character)
                            self.app.pop_screen()
                            self.app.notify("Trade rejected.")
                        except ValueError as e:
                            self.app.notify(str(e), severity="error")

                self.app.push_screen(ConfirmScreen("Reject this trade?"), callback=on_confirm_reject)
        event.stop()


class NewTradeScreen(PopScreen):
    """Select a character to trade with, then enter offer items."""

    app: "IdlemaxxApp"

    def __init__(self, character: Character) -> None:
        super().__init__()
        self.character = character

    def compose(self) -> ComposeResult:
        yield Label("Select a character to trade with")
        characters = [c for c in self.app.game.list_characters() if c.id != self.character.id]
        if characters:
            yield WrappingOptionList(
                *[Option(c.name, f"char:{c.name}") for c in characters],
                Option("⬅ Back", "__back__"),
            )
        else:
            yield WrappingOptionList(
                Option("No other characters found", "noop"),
                Option("⬅ Back", "__back__"),
            )

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        match str(event.option.id):
            case "__back__" | "noop":
                self.app.pop_screen()
            case char_key:
                char_name = char_key.removeprefix("char:")
                target = self.app.game.get_character_by_name(char_name)

                def on_offer_input(result: "tuple[dict, dict] | None") -> None:
                    if result is None:
                        return
                    give, want = result
                    try:
                        self.app.game.create_trade(self.character, target, give, want)
                        self.app.pop_screen()
                        self.app.notify(f"Trade offer sent to {target.name}!")
                    except ValueError as e:
                        self.app.notify(str(e), severity="error")

                self.app.push_screen(TradeOfferInputScreen(), callback=on_offer_input)
        event.stop()


class TradeScreen(PopScreen):
    """Trade hub: lists incoming and outgoing trades, and allows creating new ones."""

    app: "IdlemaxxApp"

    def __init__(self, character: Character) -> None:
        super().__init__()
        self.character = character

    def _other_name(self, trade: Trade) -> str:
        other_id = trade.counterparty_id if trade.initiator_id == self.character.id else trade.initiator_id
        other = self.app.game._session.get(Character, other_id)
        return other.name if other else "?"

    def _build_options(self) -> list[Option]:
        options: list[Option] = []
        incoming = self.app.game.get_pending_trades(self.character)
        outgoing = self.app.game.get_outgoing_trades(self.character)

        for t in incoming:
            n = len(t.offers)
            label = f"📥 {str(t.id)[:8]} — with {self._other_name(t)} ({n} rev) — your turn"
            options.append(Option(label, f"trade:{t.id}"))
        for t in outgoing:
            n = len(t.offers)
            label = f"📤 {str(t.id)[:8]} — with {self._other_name(t)} ({n} rev) — waiting"
            options.append(Option(label, f"trade:{t.id}"))

        if not incoming and not outgoing:
            options.append(Option("No active trades", "noop"))

        options.append(Option("➕ New trade", "new_trade"))
        options.append(Option("⬅ Back", "__back__"))
        return options

    def compose(self) -> ComposeResult:
        yield Label("Trades")
        yield WrappingOptionList(*self._build_options())

    def on_screen_resume(self) -> None:
        ol = self.query_one(WrappingOptionList)
        ol.clear_options()
        for opt in self._build_options():
            ol.add_option(opt)

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        option_id = str(event.option.id)
        match option_id:
            case "__back__":
                self.app.pop_screen()
            case "noop":
                pass
            case "new_trade":
                self.app.push_screen(NewTradeScreen(self.character))
            case trade_key if trade_key.startswith("trade:"):
                trade_id = uuid.UUID(trade_key.removeprefix("trade:"))
                trade = self.app.game.get_trade(trade_id)
                if trade is None:
                    self.app.notify("Trade not found.", severity="error")
                    self.on_screen_resume()
                    return
                self.app.push_screen(TradeDetailScreen(trade, self.character))
        event.stop()


class CharacterScreen(PopScreen):
    app: IdlemaxxApp

    def __init__(self, character: Character) -> None:
        super().__init__()
        self.character = character

    def _activity_label(self) -> str:
        act = self.character.current_activity
        if not act:
            return "Current Activity: 💤 Idle"
        category = act.activity.category
        activity_name = act.activity.value.name.replace("_", " ").title()
        label = f"Current Activity: {category.icon} {category.display_name} - {activity_name}"
        if act.action_limit is not None:
            label += f" ({act.action_limit} actions)"
        return label

    def on_screen_resume(self) -> None:
        self.query_one("#activity-label", Label).update(self._activity_label())
        self.query_one("#skills-items-count", Label).update(
            f"Skills: {len(self.character.skills)} | Items: {len(self.character.items)}",
        )

    def compose(self) -> ComposeResult:
        yield Label(f"Character: {self.character.name}")
        yield Label(
            f"Skills: {len(self.character.skills)} | Items: {len(self.character.items)}", id="skills-items-count"
        )
        yield Label(self._activity_label(), id="activity-label")
        yield WrappingOptionList(
            Option("Start activity", "start_activity"),
            Option("View skills", "view_skills"),
            Option("View items", "view_items"),
            Option("View queue", "view_queue"),
            Option("Trade", "trade"),
            Option("Delete character", "delete_char"),
            Option("Back", "back"),
        )

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        match event.option.id:
            case "start_activity":
                self.app.push_screen(ActivityCategoryScreen(self.character))
            case "view_skills":
                rows = [
                    (
                        s.skill.name,
                        level_from_xp(s.experience),
                        s.experience,
                        pendulum.instance(s.updated_at).in_tz("local").to_rfc850_string(),
                    )
                    for s in self.character.skills
                ]
                self.app.push_screen(TableViewScreen(["Skill", "Level", "Experience", "Last Modified"], rows))
            case "view_items":
                rows = [
                    (i.item.name, i.quantity, pendulum.instance(i.updated_at).in_tz("local").to_rfc850_string())
                    for i in self.character.items
                ]
                self.app.push_screen(TableViewScreen(["Item", "Quantity", "Last Modified"], rows))
            case "view_queue":
                self.app.push_screen(QueueScreen(self.character))
            case "trade":
                self.app.push_screen(TradeScreen(self.character))
            case "back":
                self.app.pop_screen()
            case "delete_char":

                def on_confirm(confirmed: bool | None) -> None:
                    if confirmed:
                        name = self.character.name
                        self.app.game.delete_character(self.character)
                        self.app.pop_screen()
                        self.app.refresh_character_menu()
                        self.app.notify(f"Deleted {name}")

                self.app.push_screen(ConfirmScreen(f"Delete {self.character.name}?"), callback=on_confirm)
            case _:
                self.app.exit()


class IdlemaxxApp(App):
    def __init__(self) -> None:
        super().__init__()
        self.game = Game("sqlite:///idlemaxx-cli.db")

    def refresh_character_menu(self) -> None:
        menu = self.query_one("#menu", OptionList)
        menu.clear_options()

        characters = self.game.list_characters()
        for char in characters:
            menu.add_option(Option(char.name, f"char:{char.name}"))
        menu.add_option(Option("Create a new character", "create_character"))
        menu.add_option(Option("Quit", "quit"))
        menu.highlighted = 0

    def on_mount(self) -> None:
        self.refresh_character_menu()

    def compose(self) -> ComposeResult:
        yield Label("Choose your character")
        yield WrappingOptionList(id="menu")

    def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
        if event.option_list.id != "menu":
            return
        option_id = str(event.option.id)
        match option_id:
            case "create_character":

                def on_name(name: str | None) -> None:
                    if name:
                        character = self.game.create_character(name)
                        self.notify(f"Created character {character.name}")
                        self.refresh_character_menu()
                    else:
                        self.exit()

                self.push_screen(CreateCharacterScreen(), callback=on_name)

            case "quit":
                self.exit()
            case _:
                char_name = option_id.removeprefix("char:")
                self.character = self.game.get_character_by_name(char_name)
                self.push_screen(CharacterScreen(self.character))


def main() -> None:
    app = IdlemaxxApp()
    app.run(inline=True)


if __name__ == "__main__":
    main()
