"""Core oracle logic - fetches data and pushes to chain."""
import asyncio
import time
from dataclasses import dataclass
from typing import Optional
from eth_account import Account
from eth_account.signers.local import LocalAccount

from .providers.base import BaseProvider, OracleData


@dataclass
class NetworkConfig:
    """Network configuration."""
    name: str
    rpc_url: str
    chain_id: int
    oracle_contract: Optional[str] = None


# Default networks
NETWORKS = {
    "testnet": NetworkConfig(
        name="NanoPy Testnet",
        rpc_url="http://51.68.125.99:8546",
        chain_id=77700,
    ),
    "turbo": NetworkConfig(
        name="Turbo L2 Testnet",
        rpc_url="http://51.68.125.99:8548",
        chain_id=777702,
    ),
}


class Oracle:
    """Main oracle class that coordinates providers and chain updates."""

    def __init__(
        self,
        private_key: str,
        provider: BaseProvider,
        network: str = "testnet",
        interval: int = 60,
    ):
        self.account: LocalAccount = Account.from_key(private_key)
        self.provider = provider
        self.network = NETWORKS.get(network)
        if not self.network:
            raise ValueError(f"Unknown network: {network}")
        self.interval = interval
        self._running = False
        self._nonce = 0

    @property
    def address(self) -> str:
        return self.account.address

    async def _get_nonce(self) -> int:
        """Get current nonce from chain."""
        import aiohttp
        async with aiohttp.ClientSession() as session:
            payload = {
                "jsonrpc": "2.0",
                "method": "eth_getTransactionCount",
                "params": [self.address, "latest"],
                "id": 1,
            }
            async with session.post(self.network.rpc_url, json=payload) as resp:
                data = await resp.json()
                return int(data["result"], 16)

    async def _send_transaction(self, data: bytes) -> str:
        """Send oracle update transaction."""
        import aiohttp

        nonce = await self._get_nonce()

        # Build transaction - store data in input field
        tx = {
            "nonce": nonce,
            "gasPrice": 1000000000,  # 1 gwei
            "gas": 100000,
            "to": self.network.oracle_contract or self.address,  # Self if no contract
            "value": 0,
            "data": data,
            "chainId": self.network.chain_id,
        }

        # Sign transaction
        signed = self.account.sign_transaction(tx)

        # Send
        async with aiohttp.ClientSession() as session:
            payload = {
                "jsonrpc": "2.0",
                "method": "eth_sendRawTransaction",
                "params": [signed.raw_transaction.hex()],
                "id": 1,
            }
            async with session.post(self.network.rpc_url, json=payload) as resp:
                result = await resp.json()
                if "error" in result:
                    raise Exception(f"TX failed: {result['error']}")
                return result["result"]

    async def push(self, key: str, **kwargs) -> tuple[OracleData, str]:
        """Fetch data and push to chain.

        Returns:
            Tuple of (OracleData, tx_hash)
        """
        # Fetch from provider
        data = await self.provider.fetch(key, **kwargs)

        # Validate
        if not await self.provider.validate(data):
            raise ValueError(f"Invalid data from provider: {data}")

        # Encode and send
        encoded = data.encode_for_chain()
        tx_hash = await self._send_transaction(encoded)

        return data, tx_hash

    async def run(self, key: str, **kwargs):
        """Run oracle in continuous mode."""
        from rich.console import Console
        console = Console()

        self._running = True
        console.print(f"[bold green]Oracle started[/bold green]")
        console.print(f"  Provider: {self.provider.name}")
        console.print(f"  Key: {key}")
        console.print(f"  Network: {self.network.name}")
        console.print(f"  Address: {self.address}")
        console.print(f"  Interval: {self.interval}s")
        console.print()

        while self._running:
            try:
                data, tx_hash = await self.push(key, **kwargs)
                console.print(
                    f"[green]✓[/green] Pushed: {data.value} "
                    f"(confidence: {data.confidence:.2f}) "
                    f"TX: {tx_hash[:16]}..."
                )
            except Exception as e:
                console.print(f"[red]✗[/red] Error: {e}")

            await asyncio.sleep(self.interval)

    def stop(self):
        """Stop the oracle."""
        self._running = False


class MultiOracle:
    """Run multiple oracles in parallel."""

    def __init__(self, oracles: list[Oracle]):
        self.oracles = oracles

    async def run_all(self, keys: list[str]):
        """Run all oracles concurrently."""
        tasks = [
            oracle.run(key)
            for oracle, key in zip(self.oracles, keys)
        ]
        await asyncio.gather(*tasks)
