import asyncio
import os
from typing import Optional

from rich.align import Align
from rich import box
from rich.console import Console
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 .sdk import EmpoorioChainConfig, EmpoorioChainSDK, Transaction

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]Chain SDK Terminal v0.1.5[/italic cyan]
"""

def _render_header():
    console.print(Panel(Text.from_markup(LOGO), box=box.SIMPLE, border_style="magenta"))

async def _render_dashboard(sdk: Optional[EmpoorioChainSDK], config: Optional[EmpoorioChainConfig]) -> None:
    console.clear()
    _render_header()
    
    if not sdk or not config:
        status_panel = Panel(
            Align.center("[bold red]● OFFLINE[/bold red]\n[dim]Connect to a node to view metrics[/dim]"),
            title="System Status",
            border_style="red",
            box=box.ROUNDED
        )
        
        details_table = Table(show_header=False, box=None, expand=True)
        details_table.add_column("Key", style="cyan")
        details_table.add_column("Value", style="white")
        details_table.add_row("RPC URL", config.rpc_url if config else "Not set")
        details_table.add_row("WS URL", config.ws_url if config and config.ws_url else "Not set")
        details_table.add_row("Chain ID", str(config.chain_id) if config else "Not set")

        config_panel = Panel(details_table, title="Configuration", border_style="blue", box=box.ROUNDED)
        
        layout = Layout()
        layout.split_row(
            Layout(status_panel, ratio=1),
            Layout(config_panel, ratio=2)
        )
        console.print(layout)
        return

    node = await sdk.get_node_info()
    latest_block = await sdk.get_latest_block()
    gas_price = await sdk.get_gas_price()

    is_syncing = node.get("syncing") not in (False, None)
    status_text = "[bold yellow]↻ SYNCING[/bold yellow]" if is_syncing else "[bold green]● ONLINE[/bold green]"
    
    # Left Panel: Network Info
    net_table = Table(show_header=False, box=None, expand=True)
    net_table.add_column("Key", style="cyan")
    net_table.add_column("Value", style="bright_white")
    net_table.add_row("Network ID", str(node.get("network_id")))
    net_table.add_row("Peers", str(node.get("peer_count")))
    net_table.add_row("Client", str(node.get("client_version")).split("/")[0]) # Shorten client name
    
    net_panel = Panel(
        Align.center(f"{status_text}\n\n") + net_table,
        title="Network Status",
        border_style="green" if not is_syncing else "yellow",
        box=box.ROUNDED
    )

    # Right Panel: Blockchain Metrics
    chain_table = Table(show_header=True, header_style="bold magenta", box=box.SIMPLE_HEAD, expand=True)
    chain_table.add_column("Metric")
    chain_table.add_column("Value", justify="right")
    
    chain_table.add_row("Height", f"[bold]{latest_block.number}[/bold]")
    chain_table.add_row("Block Hash", f"[dim]{latest_block.hash[:10]}...{latest_block.hash[-8:]}[/dim]")
    chain_table.add_row("Txs (Last Block)", str(len(latest_block.transactions)))
    chain_table.add_row("Gas Price", f"{gas_price} wei")
    chain_table.add_row("Gas Used", f"{int(latest_block.gas_used):,}")

    chain_panel = Panel(chain_table, title="Live Metrics", border_style="magenta", box=box.ROUNDED)

    layout = Layout()
    layout.split_row(
        Layout(net_panel, ratio=1),
        Layout(chain_panel, ratio=2)
    )
    console.print(layout)


async def _get_balance(sdk: EmpoorioChainSDK) -> None:
    address = Prompt.ask("Address")
    balance = await sdk.get_balance(address)
    table = Table(title="Balance", box=box.SIMPLE)
    table.add_column("Address", style="cyan")
    table.add_column("Balance", style="green")
    table.add_row(balance.address, f"{balance.balance} {balance.symbol}")
    console.print(table)


async def _get_block(sdk: EmpoorioChainSDK) -> None:
    choice = Prompt.ask("Lookup by", choices=["number", "hash"], default="number")
    if choice == "number":
        number = IntPrompt.ask("Block number")
        block = await sdk.get_block(number)
    else:
        hash_str = Prompt.ask("Block hash (0x...)")
        block = await sdk.get_block_by_hash(hash_str)

    if not block:
        console.print("[red]Block not found[/red]")
        return

    table = Table(title=f"Block {block.number}", box=box.MINIMAL_DOUBLE_HEAD)
    table.add_column("Field", style="cyan", no_wrap=True)
    table.add_column("Value", style="white")
    table.add_row("Hash", block.hash)
    table.add_row("Parent", block.parent_hash)
    table.add_row("Timestamp", str(block.timestamp))
    table.add_row("Txs", str(len(block.transactions)))
    table.add_row("Gas Used", block.gas_used)
    table.add_row("Gas Limit", block.gas_limit)
    console.print(table)


async def _get_transaction(sdk: EmpoorioChainSDK) -> None:
    tx_hash = Prompt.ask("Transaction hash (0x...)")
    tx = await sdk.get_transaction(tx_hash)
    if not tx:
        console.print("[red]Transaction not found[/red]")
        return
    table = Table(title="Transaction", box=box.MINIMAL_DOUBLE_HEAD)
    table.add_column("Field", style="cyan", no_wrap=True)
    table.add_column("Value", style="white")
    for key in [
        "hash",
        "block_number",
        "from",
        "to",
        "value",
        "gas",
        "gas_price",
        "data",
        "nonce",
    ]:
        table.add_row(key, str(tx.get(key)))
    console.print(table)


async def _estimate_gas(sdk: EmpoorioChainSDK) -> None:
    from_addr = Prompt.ask("From address")
    to_addr = Prompt.ask("To address")
    value = Prompt.ask("Value (wei)", default="0")
    data = Prompt.ask("Data (0x...)", default="0x")
    gas = await sdk.estimate_gas(
        Transaction(
            from_address=from_addr,
            to_address=to_addr,
            value=value,
            data=data,
        )
    )
    console.print(f"[green]Estimated gas:[/green] {gas}")


async def _send_transaction(sdk: EmpoorioChainSDK) -> None:
    from_addr = Prompt.ask("From address")
    to_addr = Prompt.ask("To address")
    value = Prompt.ask("Value (wei)", default="0")
    gas = Prompt.ask("Gas (optional)", default="")
    gas_price = Prompt.ask("Gas price (optional)", default="")
    data = Prompt.ask("Data (0x...)", default="0x")
    private_key = Prompt.ask("Private key (leave blank to use external signer)", default="")

    tx = Transaction(
        from_address=from_addr,
        to_address=to_addr,
        value=value,
        gas=gas if gas else None,
        gas_price=gas_price if gas_price else None,
        data=data,
    )
    tx_hash = await sdk.send_transaction(tx, private_key if private_key else None)
    console.print(f"[green]Transaction sent:[/green] {tx_hash}")


async def _wait_for_tx(sdk: EmpoorioChainSDK) -> None:
    tx_hash = Prompt.ask("Transaction hash (0x...)")
    confirmations = IntPrompt.ask("Confirmations", default=1)
    receipt = await sdk.wait_for_transaction(tx_hash, confirmations=confirmations)
    console.print("[green]Confirmed[/green]")
    console.print(receipt)


async def _subscribe_blocks(sdk: EmpoorioChainSDK) -> None:
    console.print("[yellow]Subscribing to new blocks (Ctrl+C to stop)...[/yellow]")

    def on_block(block):
        console.print(f"New block #{block.number} {block.hash}")

    await sdk.subscribe_to_new_blocks(on_block)


async def _subscribe_pending(sdk: EmpoorioChainSDK) -> None:
    console.print("[yellow]Subscribing to pending transactions (Ctrl+C to stop)...[/yellow]")

    def on_tx(tx_hash):
        console.print(f"Pending tx {tx_hash}")

    await sdk.subscribe_to_pending_transactions(on_tx)


def main() -> None:
    console.print("[bold cyan]EmpoorioChain SDK Terminal[/bold cyan]")
    config = _build_config_from_env()
    sdk: Optional[EmpoorioChainSDK] = None
    if config:
        try:
            sdk = asyncio.run(_connect_sdk(config))
        except Exception as exc:
            console.print(f"[red]Failed to connect:[/red] {exc}")

    try:
        asyncio.run(_render_dashboard(sdk, config))
        while True:
            console.print()
            
            menu_table = Table(title="Available Actions", box=box.ROUNDED, show_header=True, header_style="bold magenta")
            menu_table.add_column("ID", justify="center", style="cyan")
            menu_table.add_column("Action", style="white")
            menu_table.add_column("Description", style="dim")

            menu_table.add_row("1", "Dashboard", "Refresh view")
            menu_table.add_row("2", "Connect", "Configure RPC/WS")
            menu_table.add_section()
            menu_table.add_row("3", "Node Info", "Detailed technical info")
            menu_table.add_row("4", "Balance", "Check account balance")
            menu_table.add_row("5", "Block", "Inspect block details")
            menu_table.add_row("6", "Transaction", "Inspect TX details")
            menu_table.add_section()
            menu_table.add_row("7", "Estimate Gas", "Calculate gas costs")
            menu_table.add_row("8", "Send TX", "Execute transaction")
            menu_table.add_row("9", "Wait TX", "Poll for confirmation")
            menu_table.add_section()
            menu_table.add_row("10", "Live Blocks", "Subscribe to new blocks")
            menu_table.add_row("11", "Live TXs", "Subscribe to pending TXs")
            menu_table.add_section()
            menu_table.add_row("0", "Exit", "Close terminal")

            console.print(menu_table)
            
            choice = Prompt.ask("[bold magenta]Select Action[/bold magenta]", choices=[str(i) for i in range(12)], default="1")
            
            console.clear() # Clear before next action or re-render
            
            if choice == "0":
                console.print("[yellow]Goodbye![/yellow]")
                break
            if choice == "1":
                asyncio.run(_render_dashboard(sdk, config))
            elif choice == "2":
                config = _prompt_config()
                try:
                    sdk = asyncio.run(_connect_sdk(config))
                    console.print("[green]Connected[/green]")
                except Exception as exc:
                    console.print(f"[red]Failed to connect:[/red] {exc}")
                    sdk = None
            elif choice == "3":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                node = asyncio.run(sdk.get_node_info())
                table = Table(title="Node Info", box=box.SIMPLE)
                table.add_column("Field", style="cyan")
                table.add_column("Value", style="white")
                for key, value in node.items():
                    table.add_row(str(key), str(value))
                console.print(table)
            elif choice == "4":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                asyncio.run(_get_balance(sdk))
            elif choice == "5":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                asyncio.run(_get_block(sdk))
            elif choice == "6":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                asyncio.run(_get_transaction(sdk))
            elif choice == "7":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                asyncio.run(_estimate_gas(sdk))
            elif choice == "8":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                asyncio.run(_send_transaction(sdk))
            elif choice == "9":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                asyncio.run(_wait_for_tx(sdk))
            elif choice == "10":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                try:
                    asyncio.run(_subscribe_blocks(sdk))
                except KeyboardInterrupt:
                    console.print("\n[yellow]Stopped[/yellow]")
            elif choice == "11":
                if not sdk:
                    console.print("[red]Not connected. Use 'Configure / Connect' first.[/red]")
                    continue
                try:
                    asyncio.run(_subscribe_pending(sdk))
                except KeyboardInterrupt:
                    console.print("\n[yellow]Stopped[/yellow]")
    finally:
        try:
            asyncio.run(sdk.disconnect())
        except Exception:
            pass


if __name__ == "__main__":
    main()
