#!/usr/bin/env python3
import argparse
import json
import os
import pathlib
import subprocess
import sys
import time
import urllib.error
import urllib.request


SECTIONS = ["Dashboard", "Missions", "Active", "Community"]


def fetch_json(url):
    request = urllib.request.Request(url, headers={"accept": "application/json"})
    with urllib.request.urlopen(request, timeout=12) as response:
        return json.loads(response.read().decode("utf-8"))


def post_json(url, payload):
    data = json.dumps(payload).encode("utf-8")
    request = urllib.request.Request(
        url,
        data=data,
        headers={"content-type": "application/json", "accept": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(request, timeout=12) as response:
        return json.loads(response.read().decode("utf-8"))


def clean(value):
    return " ".join(str(value or "").split())


def count_by(items, field):
    counts = {}
    for item in items:
        key = item.get(field) or "Unknown"
        counts[key] = counts.get(key, 0) + 1
    return counts


def bar(value, total, width=16):
    if total <= 0:
        return "." * width
    filled = max(0, min(width, round((value / total) * width)))
    return "#" * filled + "." * (width - filled)


def workspace_slug(path):
    context_file = path / "context" / "action.json"
    try:
        payload = json.loads(context_file.read_text())
        return payload.get("action", {}).get("slug") or path.name.replace(
            "future-union-mission-", ""
        )
    except Exception:
        return path.name.replace("future-union-mission-", "")


def fallback_to_lite(base_url, runner_path):
    fallback = pathlib.Path(__file__).with_name("fu-control-room-lite.py")
    if fallback.exists():
        os.execv(
            sys.executable,
            [sys.executable, str(fallback), "--base", base_url, "--runner", runner_path],
        )
    print("Textual is not installed and the lite TUI fallback was not found.", file=sys.stderr)
    return 1


try:
    from rich.console import Group
    from rich.panel import Panel
    from rich.table import Table
    from rich.text import Text
    from textual.app import App, ComposeResult
    from textual.binding import Binding
    from textual.containers import Horizontal
    from textual.widgets import Static
    TEXTUAL_AVAILABLE = True
except Exception:
    TEXTUAL_AVAILABLE = False
    Group = None
    Panel = None
    Table = None
    Text = None
    App = object
    ComposeResult = object

    def Binding(*_args, **_kwargs):
        return None

    Horizontal = None
    Static = None


class FutureUnionControlRoom(App):
    CSS = """
    Screen {
        background: transparent;
    }

    #top {
        height: 2;
        padding: 0 1;
    }

    #nav {
        height: 2;
        padding: 0 1;
    }

    #main {
        height: 1fr;
        padding: 0 1;
    }

    .pane {
        height: 1fr;
        padding: 0;
        overflow: auto;
    }

    #primary {
        width: 42%;
        min-width: 42;
    }

    #detail {
        width: 38%;
        min-width: 42;
    }

    #side {
        width: 20%;
        min-width: 26;
    }

    #status {
        height: 2;
        padding: 0 1;
    }
    """

    BINDINGS = [
        Binding("1", "section('Dashboard')", "Dashboard", show=False),
        Binding("2", "section('Missions')", "Missions", show=False),
        Binding("3", "section('Active')", "Active", show=False),
        Binding("4", "section('Community')", "Community", show=False),
        Binding("j", "down", "Down"),
        Binding("k", "up", "Up"),
        Binding("enter", "run", "Open/Run"),
        Binding("s", "run", "Open/Run"),
        Binding("d", "run_default", "Run Default"),
        Binding("c", "claim", "Claim"),
        Binding("o", "workspace('room')", "Room"),
        Binding("f", "workspace('finish')", "Finish"),
        Binding("p", "workspace('experiment-preview')", "Preview"),
        Binding("shift+p", "workspace('experiment-publish')", "Publish"),
        Binding("r", "refresh", "Refresh"),
        Binding("q", "quit", "Quit"),
    ]

    def __init__(self, base_url, runner_path):
        super().__init__()
        self.base_url = base_url.rstrip("/")
        self.runner_path = runner_path
        self.section = "Dashboard"
        self.cursors = {section: 0 for section in SECTIONS}
        self.status = "Ready. Pick a mission, open Active, run one useful packet."
        self.payload = {}
        self.actions = []
        self.missions = []
        self.contributions = []
        self.intake = []
        self.runs = []
        self.priority = {}
        self.updated_at = ""
        self.active_mission_slug = ""
        self.pending_claim_slug = ""

    def compose(self) -> ComposeResult:
        yield Static(id="top")
        yield Static(id="nav")
        with Horizontal(id="main"):
            yield Static(id="primary", classes="pane")
            yield Static(id="detail", classes="pane")
            yield Static(id="side", classes="pane")
        yield Static(id="status")

    def on_mount(self):
        self.title = "Future Union Control Room"
        self.load()
        if self.missions and not self.active_mission_slug:
            self.active_mission_slug = self.missions[0].get("slug", "")
        self.refresh_view()

    def on_resize(self):
        self.apply_responsive_layout()
        self.refresh_view()

    def cursor(self):
        return self.cursors.get(self.section, 0)

    def set_cursor(self, value):
        items = self.current_items()
        if not items:
            self.cursors[self.section] = 0
        else:
            self.cursors[self.section] = max(0, min(len(items) - 1, value))

    def apply_responsive_layout(self):
        width = self.size.width
        primary = self.query_one("#primary")
        detail = self.query_one("#detail")
        side = self.query_one("#side")
        if width < 96:
            primary.styles.width = "100%"
            self.set_visible(detail, False)
            self.set_visible(side, False)
        elif width < 142:
            primary.styles.width = "46%"
            detail.styles.width = "54%"
            self.set_visible(detail, True)
            self.set_visible(side, False)
        else:
            primary.styles.width = "42%"
            detail.styles.width = "38%"
            side.styles.width = "20%"
            self.set_visible(detail, True)
            self.set_visible(side, True)

    def set_visible(self, widget, visible):
        try:
            widget.display = visible
        except Exception:
            widget.styles.display = "block" if visible else "none"

    def load(self):
        try:
            self.payload = fetch_json(f"{self.base_url}/api/mission-computer.json")
            self.priority = self.payload.get("priority", {}) or {}
            self.actions = [
                action
                for action in self.payload.get("actions", [])
                if action.get("status") != "shipped"
            ]
            self.missions = self.payload.get("missions", [])
            self.contributions = self.payload.get("contributions", [])
            self.intake = self.payload.get("intakeIdeas", [])
            self.updated_at = time.strftime("%H:%M:%S")
        except Exception as error:
            self.status = f"Could not load Mission Computer: {error}"
            self.payload = {}
            self.actions = []
            self.missions = []
            self.contributions = []
            self.intake = []
            self.priority = {}
        self.scan_runs()
        for section in SECTIONS:
            self.cursors[section] = min(self.cursors.get(section, 0), max(0, len(self.items_for(section)) - 1))

    def scan_runs(self):
        runs = []
        for path in pathlib.Path.cwd().glob("future-union-mission-*"):
            if not path.is_dir():
                continue
            slug = workspace_slug(path)
            output = path / ("ACTION_PROPOSAL.md" if slug == "action-idea-intake" else "PACKET.md")
            sources = path / "SOURCES.csv"
            room = path / ".future-union" / "rooms" / f"{slug}.md"
            source_count = 0
            if sources.exists():
                try:
                    source_count = max(0, len(sources.read_text().splitlines()) - 1)
                except Exception:
                    source_count = 0
            runs.append(
                {
                    "path": path,
                    "slug": slug,
                    "name": path.name,
                    "has_output": output.exists() and output.stat().st_size > 120,
                    "source_count": source_count,
                    "has_share": (path / "SHARE_MANIFEST.md").exists(),
                    "has_radicle": (path / "RADICLE.md").exists(),
                    "has_room": room.exists(),
                    "room": room,
                }
            )
        self.runs = sorted(runs, key=lambda run: run["name"])

    def items_for(self, section):
        if section == "Missions":
            return self.missions
        if section == "Active":
            return self.active_actions()
        if section == "Community":
            return self.contributions
        return []

    def current_items(self):
        return self.items_for(self.section)

    def active_mission(self):
        if self.section == "Missions" and self.missions:
            return self.missions[self.cursor()]
        if self.active_mission_slug:
            found = next((mission for mission in self.missions if mission.get("slug") == self.active_mission_slug), None)
            if found:
                return found
        return self.missions[0] if self.missions else None

    def active_actions(self):
        mission = self.active_mission()
        if not mission:
            return self.actions
        slug = mission.get("slug")
        cards = [
            action
            for action in self.actions
            if (action.get("parentMissionSlug") or action.get("missionSlug")) == slug
        ]
        return cards or mission.get("liveActions", []) or []

    def selected_action(self):
        actions = self.active_actions()
        if self.section == "Active" and actions:
            return actions[self.cursor()]
        if self.section == "Missions" and actions:
            return actions[0]
        return actions[0] if actions else (self.actions[0] if self.actions else None)

    def default_slug(self):
        return clean(
            self.priority.get("agentDefaultActionSlug")
            or self.priority.get("publicDefaultActionSlug")
            or self.priority.get("reviewerDefaultActionSlug")
        )

    def action_by_slug(self, slug):
        if not slug:
            return None
        return next((action for action in self.actions if action.get("slug") == slug), None)

    def default_action(self):
        return self.action_by_slug(self.default_slug()) or self.selected_action()

    def selected_run(self):
        action = self.selected_action()
        if not action:
            return None
        return next((run for run in self.runs if run["slug"] == action.get("slug")), None)

    def contributions_for_action(self, action):
        if not action:
            return []
        slug = action.get("slug")
        mission_slug = action.get("parentMissionSlug") or action.get("missionSlug")
        return [
            item
            for item in self.contributions
            if item.get("actionSlug") == slug
            or (not item.get("actionSlug") and item.get("missionSlug") == mission_slug)
        ]

    def counts_for_action(self, action):
        related = self.contributions_for_action(action)
        return {
            "claimed": sum(1 for item in related if item.get("status") == "claimed"),
            "reviewed": sum(
                1 for item in related if item.get("status") in {"reviewed", "credited", "used"}
            ),
            "radicle": sum(
                1
                for item in related
                if item.get("radiclePatchId")
                or item.get("radicleHref")
                or item.get("radicleExperimentIds")
            ),
        }

    def next_action_for(self, action):
        run = next((item for item in self.runs if item["slug"] == action.get("slug")), None)
        if not run:
            return "run"
        if not run["has_output"]:
            return "finish packet"
        if not run["has_share"]:
            return "preview"
        if not run["has_radicle"]:
            return "publish optional"
        return "review"

    def refresh_view(self):
        self.apply_responsive_layout()
        self.query_one("#top", Static).update(self.render_top())
        self.query_one("#nav", Static).update(self.render_nav())
        self.query_one("#primary", Static).update(self.render_primary())
        self.query_one("#detail", Static).update(self.render_detail())
        self.query_one("#side", Static).update(self.render_side())
        self.query_one("#status", Static).update(self.render_status())

    def render_top(self):
        title = Text("FUTURE UNION CONTROL ROOM", style="bold")
        return Text.assemble(title, Text("  "), Text(self.base_url, style="dim"), Text(f"  updated {self.updated_at}", style="dim"))

    def render_nav(self):
        chunks = []
        for index, section in enumerate(SECTIONS, start=1):
            style = "reverse bold" if section == self.section else ""
            chunks.append(Text(f" {index} {section} ", style=style))
            chunks.append(Text("  "))
        return Text.assemble(*chunks)

    def render_primary(self):
        if self.section == "Dashboard":
            return self.dashboard_panel()
        if self.section == "Missions":
            return self.missions_panel()
        if self.section == "Community":
            return self.community_panel()
        return self.active_panel()

    def dashboard_panel(self):
        total = len(self.actions)
        claimed = sum(1 for item in self.contributions if item.get("status") == "claimed")
        reviewed = sum(1 for item in self.contributions if item.get("status") in {"reviewed", "credited", "used"})
        local_runs = len(self.runs)
        table = Table.grid(expand=True)
        table.add_column(ratio=1)
        default_action = self.default_action()
        table.add_row(Text("Run the default mission", style="bold yellow"))
        if default_action:
            table.add_row(Text(clean(default_action.get("title")), style="bold green"))
            table.add_row(Text("Press d to put compute into the current bottleneck.", style="dim"))
        else:
            table.add_row(Text("Press d after the mission feed loads.", style="dim"))
        table.add_row("")
        table.add_row(Text("Make useful work legible.", style="bold"))
        table.add_row(Text("Pick a mission, run a bounded evidence pack, let review turn it into public value.", style="dim"))
        table.add_row("")
        table.add_row(self.metric_line("Open actions", total, max(total, 1)))
        table.add_row(self.metric_line("Active claims", claimed, max(total, 1)))
        table.add_row(self.metric_line("Reviewed packets", reviewed, max(len(self.contributions), 1)))
        table.add_row(self.metric_line("Local runs here", local_runs, max(total, 1)))
        table.add_row("")
        table.add_row(Text("Next: press d for default, or 2 to choose manually.", style="bold"))
        return Panel(table, title="Dashboard", border_style="dim")

    def metric_line(self, label, value, total):
        return Text(f"{label:<18} {bar(value, total, 18)} {value}")

    def missions_panel(self):
        if not self.missions:
            return Panel("No missions loaded.", title="Missions", border_style="dim")
        table = Table.grid(expand=True)
        table.add_column(ratio=3)
        table.add_column(ratio=1)
        table.add_column(ratio=1)
        for index, mission in enumerate(self.missions):
            selected = index == self.cursor()
            title_style = "reverse bold" if selected else "bold"
            live = mission.get("liveActionCount", 0)
            progress = mission.get("progressPercent", 0)
            table.add_row(
                Text(clean(mission.get("title")), style=title_style),
                Text(f"{live} live", style="dim"),
                Text(f"{progress}%", style="dim"),
            )
            table.add_row(Text(clean(mission.get("summary")), style="dim"), "", "")
            table.add_row("")
        return Panel(table, title="Missions", border_style="dim")

    def active_panel(self):
        actions = self.active_actions()
        mission = self.active_mission()
        if not actions:
            return Panel("No active cards for this mission yet.", title="Active", border_style="dim")
        table = Table.grid(expand=True)
        table.add_column(ratio=4)
        table.add_column(ratio=2)
        table.add_column(ratio=2)
        for index, action in enumerate(actions):
            counts = self.counts_for_action(action)
            selected = index == self.cursor()
            title_style = "reverse bold" if selected else "bold"
            run = next((item for item in self.runs if item["slug"] == action.get("slug")), None)
            local = "local" if run else "ready"
            table.add_row(
                Text(clean(action.get("title")), style=title_style),
                Text(clean(action.get("timeNeeded")), style="dim"),
                Text(self.next_action_for(action), style="bold" if selected else "dim"),
            )
            table.add_row(
                Text(clean(action.get("summary")), style="dim"),
                Text(local, style="dim"),
                Text(f"{counts['claimed']} active / {counts['reviewed']} reviewed", style="dim"),
            )
            table.add_row("")
        title = f"Active: {clean(mission.get('title'))}" if mission else "Active"
        return Panel(table, title=title, border_style="dim")

    def community_panel(self):
        if not self.contributions:
            return Panel("No public contribution records loaded yet.", title="Community", border_style="dim")
        table = Table.grid(expand=True)
        table.add_column(ratio=1)
        table.add_column(ratio=4)
        for index, item in enumerate(self.contributions[:24]):
            selected = index == self.cursor()
            style = "reverse bold" if selected else "bold"
            table.add_row(Text(clean(item.get("status")).upper(), style="dim"), Text(clean(item.get("title")), style=style))
            table.add_row("", Text(clean(item.get("creditReason") or item.get("summary")), style="dim"))
            table.add_row("")
        return Panel(table, title="Community", border_style="dim")

    def render_detail(self):
        if self.section == "Dashboard":
            return self.dashboard_detail()
        if self.section == "Missions":
            return self.mission_detail(self.active_mission())
        if self.section == "Community" and self.contributions:
            return self.contribution_detail(self.contributions[self.cursor()])
        return self.action_detail(self.selected_action())

    def dashboard_detail(self):
        mission = self.active_mission()
        rows = [
            Text("What this is", style="bold"),
            Text("A local control room for putting your agent or compute toward bounded Future Union work.", style="dim"),
            Text(""),
            Text("Fast path", style="bold"),
            Text("Press d to run the current default mission from the live priority feed.", style="dim"),
            Text(""),
            Text("Good contribution shape", style="bold"),
            Text("1. Pick one mission.", style="dim"),
            Text("2. Run one action.", style="dim"),
            Text("3. Return one sourced packet.", style="dim"),
            Text("4. Review decides credit.", style="dim"),
        ]
        if mission:
            rows.extend([
                Text(""),
                Text("Suggested start", style="bold"),
                Text(clean(mission.get("title")), style="dim"),
            ])
        return Panel(Group(*rows), title="Orientation", border_style="dim")

    def mission_detail(self, mission):
        if not mission:
            return Panel("No mission selected.", title="Mission", border_style="dim")
        live = mission.get("liveActions", [])
        rows = [
            Text(clean(mission.get("title")), style="bold"),
            Text(clean(mission.get("summary")), style="dim"),
            Text(""),
            Text(f"Status: {mission.get('status')} / {mission.get('timeEstimate')}", style="dim"),
            Text(f"Progress: {bar(mission.get('progressPercent', 0), 100, 22)} {mission.get('progressPercent', 0)}%"),
            Text(""),
            Text("Enter opens this mission in Active.", style="bold"),
        ]
        if live:
            rows.append(Text(""))
            rows.append(Text("Runnable cards", style="bold"))
            for action in live[:4]:
                rows.append(Text(f"- {clean(action.get('title'))}", style="dim"))
        return Panel(Group(*rows), title="Mission", border_style="dim")

    def action_detail(self, action):
        if not action:
            return Panel("No action selected.", title="Action", border_style="dim")
        counts = self.counts_for_action(action)
        run = self.selected_run()
        rows = [
            Text(clean(action.get("title")), style="bold"),
            Text(clean(action.get("summary")), style="dim"),
            Text(""),
            Text(f"Mission: {action.get('parentMissionTitle')}", style="dim"),
            Text(f"Time: {action.get('timeNeeded')} / {action.get('progress')}", style="dim"),
            Text(""),
            Text("Next", style="bold"),
            Text(self.next_action_for(action)),
            Text(""),
            Text("Output", style="bold"),
            Text(clean(action.get("outputFormat")), style="dim"),
            Text(""),
            Text("Community", style="bold"),
            Text(f"{counts['claimed']} active / {counts['reviewed']} reviewed / {counts['radicle']} network refs", style="dim"),
        ]
        if run:
            states = [
                "packet" if run["has_output"] else "draft",
                f"{run['source_count']} sources",
                "preview" if run["has_share"] else "no preview",
                "room" if run["has_room"] else "no room",
            ]
            rows.extend([Text(""), Text("Local run", style="bold"), Text(" / ".join(states), style="dim")])
        return Panel(Group(*rows), title="Action", border_style="dim")

    def contribution_detail(self, contribution):
        rows = [
            Text(clean(contribution.get("title")), style="bold"),
            Text(clean(contribution.get("summary")), style="dim"),
            Text(""),
            Text(f"Contributor: {contribution.get('contributor')}", style="dim"),
            Text(f"Status: {contribution.get('status')} / {contribution.get('outputType')}", style="dim"),
            Text(""),
            Text("Credit is proportional to reviewed usefulness.", style="bold"),
        ]
        return Panel(Group(*rows), title="Contribution", border_style="dim")

    def render_side(self):
        if self.section == "Community":
            return self.credit_side()
        if self.section == "Dashboard":
            return self.signal_side()
        return self.command_side()

    def signal_side(self):
        lanes = count_by(self.actions, "columnTitle")
        rows = [Text("Queue", style="bold")]
        for lane, count in lanes.items():
            rows.append(Text(f"{lane[:14]:<14} {bar(count, len(self.actions), 8)} {count}", style="dim"))
        rows.extend([
            Text(""),
            Text("Local", style="bold"),
            Text(f"runs      {len(self.runs)}", style="dim"),
            Text(f"packets   {sum(1 for run in self.runs if run['has_output'])}", style="dim"),
            Text(f"previews  {sum(1 for run in self.runs if run['has_share'])}", style="dim"),
        ])
        return Panel(Group(*rows), title="Signal", border_style="dim")

    def command_side(self):
        rows = [
            Text("Keys", style="bold"),
            Text("1 Dashboard", style="dim"),
            Text("2 Missions", style="dim"),
            Text("3 Active", style="dim"),
            Text("4 Community", style="dim"),
            Text(""),
            Text("Enter open/run", style="dim"),
            Text("d run default", style="dim"),
            Text("c claim", style="dim"),
            Text("o room", style="dim"),
            Text("p preview", style="dim"),
            Text("P publish", style="dim"),
            Text("r refresh", style="dim"),
        ]
        return Panel(Group(*rows), title="Controls", border_style="dim")

    def credit_side(self):
        rows = [
            Text("Community", style="bold"),
            Text("This should feel alive, but credit is never automatic.", style="dim"),
            Text(""),
            Text("Active claim: someone started.", style="dim"),
            Text("Reviewed: useful packet accepted.", style="dim"),
            Text("Radicle: optional public packet lane.", style="dim"),
        ]
        return Panel(Group(*rows), title="Credit", border_style="dim")

    def render_status(self):
        return Text.assemble(
            Text("d default  j/k move  Enter open/run  c claim  o room  p preview  P publish  r refresh  q quit", style="dim"),
            Text("\n"),
            Text(self.status),
        )

    def action_section(self, section):
        self.section = section
        self.set_cursor(self.cursor())
        self.status = self.section_prompt(section)
        self.refresh_view()

    def section_prompt(self, section):
        return {
            "Dashboard": "Dashboard: one view of what to do next.",
            "Missions": "Missions: choose a workstream, Enter opens it.",
            "Active": "Active: run one mission card and return a packet.",
            "Community": "Community: visible contribution, reviewed credit.",
        }.get(section, "")

    def action_down(self):
        self.set_cursor(self.cursor() + 1)
        self.refresh_view()

    def action_up(self):
        self.set_cursor(self.cursor() - 1)
        self.refresh_view()

    def action_refresh(self):
        self.status = "Refreshing Mission Computer..."
        self.load()
        self.status = "Refreshed."
        self.refresh_view()

    def action_run(self):
        if self.section == "Dashboard":
            self.section = "Missions"
            self.status = "Choose a mission, then Enter."
            self.refresh_view()
            return
        if self.section == "Missions":
            mission = self.active_mission()
            if mission:
                self.active_mission_slug = mission.get("slug", "")
                self.section = "Active"
                self.cursors["Active"] = 0
                self.status = f"Active mission: {clean(mission.get('title'))}"
            self.refresh_view()
            return
        if self.section != "Active":
            self.status = "Switch to Missions or Active to run work."
            self.refresh_view()
            return
        action = self.selected_action()
        if not action:
            self.status = "No action selected."
            self.refresh_view()
            return
        self.exit(("run-action", action.get("slug")))

    def action_run_default(self):
        action = self.default_action()
        if not action:
            self.status = "No default mission is available yet."
            self.refresh_view()
            return
        self.exit(("run-action", action.get("slug")))

    def action_claim(self):
        action = self.selected_action()
        if not action:
            return
        slug = action.get("slug")
        if self.pending_claim_slug != slug:
            self.pending_claim_slug = slug
            self.status = f"Press c again to record a public active-run claim for {action.get('title')}."
            self.refresh_view()
            return
        payload = {
            "consent": "yes",
            "missionSlug": action.get("parentMissionSlug") or action.get("missionSlug"),
            "actionSlug": slug,
            "actionTitle": action.get("title"),
            "missionTitle": action.get("parentMissionTitle"),
            "contributorName": os.environ.get("FU_CONTRIBUTOR_NAME", ""),
            "contributorHandle": os.environ.get("FU_CONTRIBUTOR_HANDLE", ""),
            "contact": os.environ.get("FU_CONTACT", ""),
            "anonymous": os.environ.get("FU_ANONYMOUS", "yes"),
            "agent": os.environ.get("FU_AGENT", "local runner"),
        }
        try:
            post_json(f"{self.base_url}/api/contributions/claims", payload)
            self.status = "Active claim recorded. Reviewed credit still needs a useful packet."
            self.pending_claim_slug = ""
            self.load()
        except urllib.error.HTTPError as error:
            self.status = f"Claim failed: HTTP {error.code}"
        except Exception as error:
            self.status = f"Claim failed: {error}"
        self.refresh_view()

    def action_workspace(self, command):
        run = self.selected_run()
        if not run:
            self.status = "Run this action first, then use workspace commands."
            self.refresh_view()
            return
        self.exit(("workspace-command", str(run["path"]), command))


def run_textual(args):
    app = FutureUnionControlRoom(args.base, args.runner)
    return app.run()


def run_result_loop(args):
    while True:
        result = run_textual(args)
        if not result:
            return 0
        if result[0] == "run-action":
            env = os.environ.copy()
            env["FU_PICK"] = "0"
            env["FU_BASE_URL"] = args.base
            subprocess.run(["sh", args.runner, result[1]], env=env, check=False)
        elif result[0] == "workspace-command":
            subprocess.run(["./bin/fu-mission", result[2]], cwd=result[1], check=False)
        try:
            input("\nPress Enter to return to Future Union Control Room...")
        except EOFError:
            return 0


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Future Union local Control Room")
    parser.add_argument("--base", default=os.environ.get("FU_BASE_URL", "https://www.futureunion.org.uk"))
    parser.add_argument("--runner", default=os.environ.get("FU_RUNNER_PATH", "./fu-runner.sh"))
    parsed = parser.parse_args()
    if not TEXTUAL_AVAILABLE:
        raise SystemExit(fallback_to_lite(parsed.base, parsed.runner))
    raise SystemExit(run_result_loop(parsed))
