#!/usr/bin/env python3
"""NanoPy Oracle CLI - AI-powered blockchain oracle."""
import asyncio
import click
from rich.console import Console
from rich.panel import Panel

from .oracle import Oracle, NETWORKS
from .providers import SentimentProvider, PriceProvider, LLMProvider, ChatProvider


@click.command()
@click.option("--private-key", envvar="ORACLE_KEY", required=True, help="Oracle wallet private key")
@click.option("--provider", type=click.Choice(["sentiment", "price", "llm", "chat"]), default="sentiment", help="Data provider")
@click.option("--key", default="default", help="Data key (e.g., 'twitter:nanopy' or 'ETH/USD')")
@click.option("--network", type=click.Choice(["testnet", "turbo"]), default="testnet", help="Target network")
@click.option("--interval", default=60, help="Update interval in seconds")
@click.option("--use-ml/--no-ml", default=True, help="Use ML models (requires transformers)")
@click.option("--text", default=None, help="Text to analyze (for sentiment provider)")
@click.option("--prompt", default=None, help="Prompt for LLM provider")
@click.option("--model", default="Pacific-Prime/pacific-prime", help="HuggingFace model name for LLM provider")
@click.option("--max-tokens", default=100, help="Max tokens to generate for LLM")
@click.option("--once", is_flag=True, help="Run once and exit")
def main(
    private_key: str,
    provider: str,
    key: str,
    network: str,
    interval: int,
    use_ml: bool,
    text: str,
    prompt: str,
    model: str,
    max_tokens: int,
    once: bool,
):
    """Start the NanoPy Oracle.

    Examples:

        # Run sentiment oracle
        nanopy-oracle run --provider sentiment --key "twitter:nanopy" --text "NanoPy is great!"

        # Run price oracle
        nanopy-oracle run --provider price --key "ETH/USD" --interval 300

        # Run LLM oracle with Pacific-Prime
        nanopy-oracle run --provider llm --prompt "What is blockchain?" --once

        # Run chat oracle
        nanopy-oracle run --provider chat --prompt "Explain NanoPy" --once
    """
    console = Console()

    # Create provider
    if provider == "sentiment":
        data_provider = SentimentProvider(use_ml=use_ml)
    elif provider == "price":
        data_provider = PriceProvider()
    elif provider == "llm":
        data_provider = LLMProvider(model_name=model, max_tokens=max_tokens)
    elif provider == "chat":
        data_provider = ChatProvider(model_name=model, max_tokens=max_tokens)
    else:
        raise ValueError(f"Unknown provider: {provider}")

    # Create oracle
    oracle = Oracle(
        private_key=private_key,
        provider=data_provider,
        network=network,
        interval=interval,
    )

    # Display info
    net = NETWORKS[network]
    model_info = f"\n[dim]Model:[/dim]     {model}" if provider in ["llm", "chat"] else ""
    console.print(Panel(f"""
[bold cyan]NanoPy Oracle[/bold cyan]

[dim]Provider:[/dim]  {provider}
[dim]Key:[/dim]       {key}
[dim]Network:[/dim]   {net.name} (Chain ID: {net.chain_id})
[dim]Address:[/dim]   {oracle.address}
[dim]Interval:[/dim]  {interval}s{model_info}

[dim]Press Ctrl+C to stop[/dim]
""", title="[bold green]Oracle Ready[/bold green]", border_style="green"))

    # Prepare kwargs
    kwargs = {}
    if text:
        kwargs["text"] = text
    if prompt:
        kwargs["prompt"] = prompt

    try:
        if once:
            # Single run
            async def run_once():
                data, tx_hash = await oracle.push(key, **kwargs)
                console.print(f"\n[bold green]Success![/bold green]")
                console.print(f"  Value: {data.value}")
                console.print(f"  Confidence: {data.confidence:.2%}")
                console.print(f"  TX Hash: {tx_hash}")
                if data.metadata:
                    console.print(f"  Metadata: {data.metadata}")

            asyncio.run(run_once())
        else:
            # Continuous mode
            asyncio.run(oracle.run(key, **kwargs))
    except KeyboardInterrupt:
        console.print("\n[yellow]Oracle stopped[/yellow]")
    except Exception as e:
        console.print(f"\n[red]Error: {e}[/red]")
        raise


@click.command("test")
@click.option("--provider", type=click.Choice(["sentiment", "price", "llm", "chat"]), default="sentiment")
@click.option("--text", default="NanoPy blockchain is amazing! Bullish on NPY!")
@click.option("--prompt", default="What is NanoPy?")
@click.option("--model", default="Pacific-Prime/pacific-prime")
@click.option("--max-tokens", default=50)
def test_provider(provider: str, text: str, prompt: str, model: str, max_tokens: int):
    """Test a provider without sending to chain."""
    console = Console()

    async def run_test():
        if provider == "sentiment":
            p = SentimentProvider(use_ml=False)
            data = await p.fetch("test", text=text)
        elif provider == "price":
            p = PriceProvider()
            data = await p.fetch("ETH/USD")
        elif provider == "llm":
            p = LLMProvider(model_name=model, max_tokens=max_tokens)
            data = await p.fetch("test", prompt=prompt)
        elif provider == "chat":
            p = ChatProvider(model_name=model, max_tokens=max_tokens)
            data = await p.fetch("test", prompt=prompt)
        else:
            raise ValueError(f"Unknown provider: {provider}")

        console.print(Panel(f"""
[bold]Provider Test Results[/bold]

[dim]Provider:[/dim]   {data.provider}
[dim]Key:[/dim]        {data.key}
[dim]Value:[/dim]      {data.value}
[dim]Confidence:[/dim] {data.confidence:.2%}
[dim]Timestamp:[/dim]  {data.timestamp}
[dim]Metadata:[/dim]   {data.metadata}
""", border_style="cyan"))

    asyncio.run(run_test())


# Group commands
@click.group()
def cli():
    """NanoPy Oracle - AI-powered blockchain oracle."""
    pass


cli.add_command(main, name="run")
cli.add_command(test_provider, name="test")


@click.command("serve")
@click.option("--host", default="0.0.0.0", help="Host to bind to")
@click.option("--port", default=8080, help="Port to listen on")
@click.option("--model", default="Qwen/Qwen2.5-0.5B-Instruct", help="Default model")
@click.option("--oracle-key", envvar="ORACLE_KEY", default=None, help="Private key for on-chain submission")
def serve_web(host: str, port: int, model: str, oracle_key: str):
    """Start the web interface with API server."""
    import os
    from http.server import HTTPServer, SimpleHTTPRequestHandler
    from urllib.parse import parse_qs, urlparse
    import json
    import threading

    console = Console()

    # Oracle contract config
    ORACLE_CONTRACT = "0xab8411ddb88b15cafb229ca387779e94a7fdf2e8"
    RPC_URL = "http://51.68.125.99:8546"

    # Setup web3 for on-chain submission if key provided
    oracle_account = None
    if oracle_key:
        try:
            from eth_account import Account
            oracle_account = Account.from_key(oracle_key)
            console.print(f"[green]Oracle wallet loaded: {oracle_account.address}[/green]")
        except Exception as e:
            console.print(f"[yellow]Could not load oracle key: {e}[/yellow]")

    # Load model once
    console.print(f"[cyan]Loading model: {model}[/cyan]")
    llm_provider = LLMProvider(model_name=model, max_tokens=100)

    # Get static folder path
    static_dir = os.path.join(os.path.dirname(__file__), "static")

    class OracleHandler(SimpleHTTPRequestHandler):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, directory=static_dir, **kwargs)

        def do_POST(self):
            content_length = int(self.headers.get("Content-Length", 0))
            body = self.rfile.read(content_length)
            data = json.loads(body)

            if self.path == "/api/oracle/query":
                prompt = data.get("prompt", "Hello")
                req_model = data.get("model", model)
                max_tokens = data.get("max_tokens", 100)

                # Use different model if requested
                if req_model != llm_provider.model_name:
                    provider = LLMProvider(model_name=req_model, max_tokens=max_tokens)
                else:
                    provider = llm_provider

                # Get response
                async def get_response():
                    return await provider.fetch("query", prompt=prompt)

                result = asyncio.run(get_response())

                # Send response
                response = {
                    "response": result.value,
                    "confidence": result.confidence,
                    "tokens": result.metadata.get("response_length", 0),
                    "model": req_model
                }

                self.send_response(200)
                self.send_header("Content-Type", "application/json")
                self.send_header("Access-Control-Allow-Origin", "*")
                self.end_headers()
                self.wfile.write(json.dumps(response).encode())

            elif self.path == "/api/oracle/submit":
                # Handle signed oracle submission from MetaMask
                message = data.get("message", "")
                signature = data.get("signature", "")
                signer = data.get("signer", "")

                console.print(f"[cyan]Received signed oracle data from {signer[:10]}...[/cyan]")
                console.print(f"[dim]Signature: {signature[:20]}...[/dim]")

                # Verify signature using eth_account
                try:
                    from eth_account.messages import encode_defunct
                    from eth_account import Account

                    # Recover signer from signature
                    message_hash = encode_defunct(text=message)
                    recovered = Account.recover_message(message_hash, signature=signature)

                    if recovered.lower() != signer.lower():
                        self.send_response(400)
                        self.send_header("Content-Type", "application/json")
                        self.send_header("Access-Control-Allow-Origin", "*")
                        self.end_headers()
                        self.wfile.write(json.dumps({"error": "Invalid signature"}).encode())
                        return

                    console.print(f"[green]Signature verified for {recovered}[/green]")

                    # Parse oracle data from message
                    oracle_data = json.loads(message)

                    # Store or broadcast to blockchain here
                    # For now, just log it
                    console.print(f"[bold green]Oracle data accepted![/bold green]")
                    console.print(f"  Key: {oracle_data.get('key')}")
                    console.print(f"  Response: {oracle_data.get('response', '')[:50]}...")
                    console.print(f"  Confidence: {oracle_data.get('confidence')}%")

                    # Return success with a mock tx hash (in production, this would be the real tx)
                    tx_hash = "0x" + signature[2:66]  # Use part of signature as mock tx hash

                    self.send_response(200)
                    self.send_header("Content-Type", "application/json")
                    self.send_header("Access-Control-Allow-Origin", "*")
                    self.end_headers()
                    self.wfile.write(json.dumps({
                        "success": True,
                        "tx_hash": tx_hash,
                        "signer": recovered,
                        "message": "Oracle data submitted successfully"
                    }).encode())

                except ImportError:
                    # eth_account not available, accept anyway in demo mode
                    console.print("[yellow]eth_account not available, accepting in demo mode[/yellow]")
                    tx_hash = "0x" + signature[2:66]

                    self.send_response(200)
                    self.send_header("Content-Type", "application/json")
                    self.send_header("Access-Control-Allow-Origin", "*")
                    self.end_headers()
                    self.wfile.write(json.dumps({
                        "success": True,
                        "tx_hash": tx_hash,
                        "signer": signer,
                        "message": "Oracle data accepted (demo mode)"
                    }).encode())

                except Exception as e:
                    console.print(f"[red]Error processing submission: {e}[/red]")
                    self.send_response(500)
                    self.send_header("Content-Type", "application/json")
                    self.send_header("Access-Control-Allow-Origin", "*")
                    self.end_headers()
                    self.wfile.write(json.dumps({"error": str(e)}).encode())

            else:
                self.send_error(404)

        def do_OPTIONS(self):
            self.send_response(200)
            self.send_header("Access-Control-Allow-Origin", "*")
            self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
            self.send_header("Access-Control-Allow-Headers", "Content-Type")
            self.end_headers()

        def log_message(self, format, *args):
            console.print(f"[dim]{args[0]}[/dim]")

    console.print(Panel(f"""
[bold cyan]NanoPy Oracle Web Interface[/bold cyan]

[dim]URL:[/dim]      http://{host}:{port}
[dim]Model:[/dim]    {model}
[dim]Static:[/dim]   {static_dir}

[bold green]Open your browser to start querying![/bold green]

[dim]Press Ctrl+C to stop[/dim]
""", title="[bold green]Server Ready[/bold green]", border_style="green"))

    try:
        server = HTTPServer((host, port), OracleHandler)
        server.serve_forever()
    except KeyboardInterrupt:
        console.print("\n[yellow]Server stopped[/yellow]")


cli.add_command(serve_web, name="serve")


if __name__ == "__main__":
    cli()
