import asyncio
import os
import sys
import tty
import termios
from typing import Optional, List
from datetime import datetime

from rich import box
from rich.align import Align
from rich.console import Console, Group
from rich.layout import Layout
from rich.panel import Panel
from rich.prompt import Prompt, IntPrompt
from rich.table import Table
from rich.text import Text
from rich.live import Live
from rich.style import Style

from .sdk import EmpoorioChainConfig, EmpoorioChainSDK, Transaction

console = Console()

# Compact Logo
LOGO = """[bold magenta]███████╗███╗   ███╗██████╗  ██████╗  ██████╗ ██████╗ [/bold magenta]
[bold magenta]██╔════╝████╗ ████║██╔══██╗██╔═══██╗██╔═══██╗██╔══██╗[/bold magenta]
[bold magenta]█████╗  ██╔████╔██║██████╔╝██║   ██║██║   ██║██████╔╝[/bold magenta]
[bold magenta]██╔══╝  ██║╚██╔╝██║██╔═══╝ ██║   ██║██║   ██║██╔══██╗[/bold magenta]
[bold magenta]███████╗██║ ╚═╝ ██║██║     ╚██████╔╝╚██████╔╝██║  ██║[/bold magenta]
[bold magenta]╚══════╝╚═╝     ╚═╝╚═╝      ╚═════╝  ╚═════╝ ╚═╝  ╚═╝[/bold magenta] [italic cyan]Terminal v0.1.9[/italic cyan]"""

class Getch:
    """Gets a single character from standard input.  Does not echo to the screen."""
    def __call__(self):
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
            if ch == '\x1b':
                ch2 = sys.stdin.read(1)
                if ch2 == '[':
                    ch3 = sys.stdin.read(1)
                    if ch3 == 'A': return 'UP'
                    if ch3 == 'B': return 'DOWN'
            if ch == '\r' or ch == '\n': return 'ENTER'
            return ch
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

class DashboardApp:
    def __init__(self):
        self.config: Optional[EmpoorioChainConfig] = self._build_config_from_env()
        self.sdk: Optional[EmpoorioChainSDK] = None
        self.layout = Layout()
        self.connected = False
        self.make_layout()
        
    def _env(self, name: str, default: Optional[str] = None) -> Optional[str]:
        value = os.getenv(name, default)
        return value if value and value.strip() else default

    def _build_config_from_env(self) -> Optional[EmpoorioChainConfig]:
        rpc_url = self._env("EMPOORIO_RPC_URL", "http://127.0.0.1:8545")
        if not rpc_url:
            return None
        ws_url = self._env("EMPOORIO_WS_URL")
        chain_id = int(self._env("EMPOORIO_CHAIN_ID", "1234"))
        return EmpoorioChainConfig(rpc_url=rpc_url, ws_url=ws_url, chain_id=chain_id)

    async def connect(self):
        if not self.config:
            return
        try:
            self.sdk = EmpoorioChainSDK(self.config)
            await self.sdk.connect()
            self.connected = True
        except Exception:
            self.connected = False
            self.sdk = None

    def make_layout(self) -> Layout:
        self.layout.split(
            Layout(name="header", size=8), # Compact header
            Layout(name="main", ratio=1),
            Layout(name="menu", size=10), # Dedicated menu space
        )
        self.layout["main"].split_row(
            Layout(name="network", ratio=1),
            Layout(name="metrics", ratio=2),
        )
        return self.layout

    async def update_view(self, menu_options: List[dict], selected_index: int) -> Panel:
        # Header
        self.layout["header"].update(Panel(Align.center(Text.from_markup(LOGO)), style="bold magenta", box=box.ROUNDED, padding=(0,1)))
        
        # Connection Status & Data
        if not self.connected or not self.sdk:
            status_panel = Panel(
                Align.center("\n[bold red]● DISCONNECTED[/bold red]\n[dim]No Connection[/dim]"),
                title="[bold red]Status[/bold red]",border_style="red", box=box.ROUNDED
            )
            metrics_content = Align.center("[dim]Waiting for RPC...[/dim]")
        else:
            try:
                node = await self.sdk.get_node_info()
                latest_block = await self.sdk.get_latest_block()
                gas_price = await self.sdk.get_gas_price()
                
                is_syncing = node.get("syncing") not in (False, None)
                status_text = "[bold yellow]⚡ SYNC[/bold yellow]" if is_syncing else "[bold green]● LIVE[/bold green]"
                
                # Network Info
                net_table = Table(show_header=False, box=None, padding=(0, 1), collapse_padding=True)
                net_table.add_column(style="cyan bold")
                net_table.add_column(style="white")
                net_table.add_row("Client", str(node.get("client_version")).split("/")[0])
                net_table.add_row("Peers", f"{node.get('peer_count')}")
                net_table.add_row("Chain", f"{node.get('chain_id')}")
                
                status_panel = Panel(
                    Align.center(f"{status_text}\n") + net_table,
                    title="[bold green]Node[/bold green]", border_style="green", box=box.ROUNDED
                )
                
                # Metrics Info
                chain_table = Table(show_header=True, header_style="bold magenta", box=box.SIMPLE_HEAD, expand=True)
                chain_table.add_column("METRIC", style="dim")
                chain_table.add_column("VALUE", justify="right", style="bold")
                chain_table.add_row("Block", f"[cyan]{latest_block.number:,}[/cyan]")
                chain_table.add_row("Time", datetime.fromtimestamp(latest_block.timestamp).strftime('%H:%M:%S'))
                chain_table.add_row("Txs", f"[green]{len(latest_block.transactions)}[/green]")
                chain_table.add_row("Gas", f"[yellow]{gas_price}[/yellow]")
                
                metrics_content = chain_table
                
            except Exception:
                self.connected = False
                status_panel = Panel("Error", style="red")
                metrics_content = Text("Connection Lost")

        self.layout["network"].update(status_panel)
        self.layout["metrics"].update(Panel(metrics_content, title="[bold magenta]Metrics[/bold magenta]", border_style="magenta", box=box.ROUNDED))

        # Render Menu Table
        menu_table = Table(box=box.SIMPLE, show_header=False, expand=True)
        menu_table.add_column("Pointer", justify="right", width=2)
        menu_table.add_column("Option", style="bold cyan")
        menu_table.add_column("Desc", style="dim")

        for idx, opt in enumerate(menu_options):
            if idx == selected_index:
                pointer = "➤"
                style = "bold white on blue"
                desc_style = "white on blue"
            else:
                pointer = " "
                style = "dim"
                desc_style = "dim"
            
            menu_table.add_row(pointer, Text(opt["label"], style=style), Text(opt["desc"], style=desc_style))

        self.layout["menu"].update(Panel(menu_table, title="[bold]Menu (Use ↑/↓ + Enter)[/bold]", border_style="blue", box=box.DOUBLE))
        
        return self.layout

async def interactive_loop():
    app = DashboardApp()
    await app.connect()
    
    getch = Getch()
    
    options = [
        {"id": "refresh", "label": "REFRESH", "desc": "Update metrics"},
        {"id": "connect", "label": "CONNECT", "desc": "Configure Node"},
        {"id": "wallet", "label": "WALLET", "desc": "Check Balance"},
        {"id": "explorer", "label": "EXPLORER", "desc": "Lookup Data"},
        {"id": "transfer", "label": "TRANSFER", "desc": "Send Funds"},
        {"id": "exit", "label": "EXIT", "desc": "Quit"},
    ]
    selected_index = 0

    with Live(app.layout, refresh_per_second=4, screen=True) as live:
        while True:
            # Update UI
            await app.update_view(options, selected_index)
            live.update(app.layout)
            
            # Blocking input (rudimentary) -> In a real async TUI we'd use a separate thread or library
            # But here we pause the loop to wait for keypress. 
            # Ideally 'rich.live' runs in background, but standard input blocks. 
            # We will accept blocking for specific user action.
            
            key = getch()
            
            if key == 'UP':
                selected_index = max(0, selected_index - 1)
            elif key == 'DOWN':
                selected_index = min(len(options) - 1, selected_index + 1)
            elif key == "q":
                break
            elif key == 'ENTER':
                choice = options[selected_index]["id"]
                
                if choice == "exit":
                    break
                elif choice == "refresh":
                    await app.connect()
                elif choice == "connect":
                    live.stop()
                    console.clear()
                    rpc = Prompt.ask("[bold]RPC URL[/bold]", default="http://127.0.0.1:8545")
                    ws = Prompt.ask("[bold]WS URL[/bold] (optional)", default="")
                    cid = IntPrompt.ask("[bold]Chain ID[/bold]", default=1234)
                    app.config = EmpoorioChainConfig(rpc_url=rpc, ws_url=ws if ws else None, chain_id=cid)
                    await app.connect()
                    live.start()
                elif choice == "wallet":
                    live.stop()
                    console.clear() 
                    if not app.connected:
                         console.print("[red]Connect first![/red]")
                    else:
                        addr = Prompt.ask("Address")
                        try:
                            bal = await app.sdk.get_balance(addr)
                            console.print(Panel(f"[bold green]{bal.balance} {bal.symbol}[/bold green]", title="Balance", border_style="green"))
                        except Exception as e:
                            console.print(f"[red]Error: {e}[/red]")
                    Prompt.ask("Press Enter to return")
                    live.start()
                elif choice == "explorer":
                    live.stop()
                    console.clear()
                    if not app.connected:
                         console.print("[red]Connect first![/red]")
                    else:
                         mode = Prompt.ask("Search", choices=["block", "tx"], default="block")
                         try:
                             if mode == "block":
                                 num = IntPrompt.ask("Block Number")
                                 console.print(await app.sdk.get_block(num))
                             else:
                                 h = Prompt.ask("Tx Hash")
                                 console.print(await app.sdk.get_transaction(h))
                         except Exception as e:
                             console.print(f"[red]Error: {e}[/red]")
                    Prompt.ask("Press Enter to return")
                    live.start()
                elif choice == "transfer":
                    live.stop()
                    console.clear()
                    if not app.connected:
                         console.print("[red]Connect first![/red]")
                    else:
                         t = Prompt.ask("Recipient")
                         v = Prompt.ask("Amount (wei)")
                         pk = Prompt.ask("Private Key", password=True)
                         try:
                             tx = await app.sdk.send_transaction(Transaction("", t, v), pk)
                             console.print(f"[green]Sent: {tx}[/green]")
                         except Exception as e:
                             console.print(f"[red]Error: {e}[/red]")
                    Prompt.ask("Press Enter to return")
                    live.start()

def main():
    try:
        asyncio.run(interactive_loop())
    except KeyboardInterrupt:
        pass

if __name__ == "__main__":
    main()

