"""
Blockchain Event Indexer

This service continuously polls the blockchain for new events emitted by
BetEscrow and BetOracle contracts, then syncs them to the PostgreSQL database.

This allows the hybrid architecture to:
1. Use blockchain as source of truth for escrow and settlement
2. Maintain fast queries through cached database records
3. Provide real-time updates to users via WebSocket

NOTE: This is pseudocode/skeleton showing the architecture.
"""

import asyncio
from typing import Dict, Any, List
from datetime import datetime


class BlockchainIndexer:
    """
    Indexes blockchain events and syncs to database.

    Continuously polls for new blocks, extracts events, and updates
    the PostgreSQL database to match on-chain state.
    """

    def __init__(self, blockchain_service, database_session):
        """
        Initialize indexer.

        Args:
            blockchain_service: Instance of BlockchainService
            database_session: SQLAlchemy async session
        """
        self.blockchain_service = blockchain_service
        self.db = database_session
        self.is_running = False
        self.poll_interval = 10  # seconds

    async def start(self):
        """
        Start the indexer background worker.

        This runs continuously, polling for new blocks every 10 seconds.
        """
        self.is_running = True
        print("Blockchain indexer started")

        try:
            while self.is_running:
                await self._index_new_blocks()
                await asyncio.sleep(self.poll_interval)
        except Exception as e:
            print(f"Indexer error: {e}")
            self.is_running = False

    async def stop(self):
        """Stop the indexer."""
        self.is_running = False
        print("Blockchain indexer stopped")

    async def _index_new_blocks(self):
        """
        Index all new blocks since last indexed block.

        Flow:
        1. Get last indexed block from database
        2. Get current blockchain height
        3. For each new block, extract and process events
        4. Update last indexed block
        """
        # Pseudocode:
        #
        # # Get last indexed block from database
        # last_indexed_block = await self.db.execute(
        #     select(BlockchainSync).order_by(BlockchainSync.block_number.desc()).limit(1)
        # )
        # last_block = last_indexed_block.scalar_one_or_none()
        #
        # if last_block:
        #     start_block = last_block.block_number + 1
        # else:
        #     # First time indexing - start from contract deployment block
        #     start_block = DEPLOYMENT_BLOCK_NUMBER
        #
        # # Get current blockchain height
        # current_block = await self.blockchain_service.web3.eth.block_number
        #
        # if start_block > current_block:
        #     return  # No new blocks
        #
        # print(f"Indexing blocks {start_block} to {current_block}")
        #
        # # Index each block
        # for block_num in range(start_block, current_block + 1):
        #     await self._index_block(block_num)
        #
        # # Update last indexed block
        # sync_record = BlockchainSync(
        #     block_number=current_block,
        #     indexed_at=datetime.utcnow()
        # )
        # self.db.add(sync_record)
        # await self.db.commit()

        # Placeholder for pseudocode
        print("[Indexer] Checking for new blocks...")
        await asyncio.sleep(0.1)

    async def _index_block(self, block_number: int):
        """
        Index all events in a specific block.

        Args:
            block_number: Block number to index
        """
        # Pseudocode:
        #
        # # Get BetCreated events
        # bet_created_events = await self.blockchain_service.bet_escrow_contract.events.BetCreated.getLogs(
        #     fromBlock=block_number,
        #     toBlock=block_number
        # )
        #
        # for event in bet_created_events:
        #     await self._handle_bet_created(event)
        #
        # # Get BetMatched events
        # bet_matched_events = await self.blockchain_service.bet_escrow_contract.events.BetMatched.getLogs(
        #     fromBlock=block_number,
        #     toBlock=block_number
        # )
        #
        # for event in bet_matched_events:
        #     await self._handle_bet_matched(event)
        #
        # # Get BetSettled events
        # bet_settled_events = await self.blockchain_service.bet_escrow_contract.events.BetSettled.getLogs(
        #     fromBlock=block_number,
        #     toBlock=block_number
        # )
        #
        # for event in bet_settled_events:
        #     await self._handle_bet_settled(event)
        #
        # # Get BetDisputed events
        # bet_disputed_events = await self.blockchain_service.bet_escrow_contract.events.BetDisputed.getLogs(
        #     fromBlock=block_number,
        #     toBlock=block_number
        # )
        #
        # for event in bet_disputed_events:
        #     await self._handle_bet_disputed(event)

        print(f"[Indexer] Indexing block {block_number}")
        await asyncio.sleep(0.1)

    async def _handle_bet_created(self, event: Dict[str, Any]):
        """
        Handle BetCreated event.

        Event structure:
        {
            'args': {
                'betId': 42,
                'creator': '0xAlice...',
                'stakeAmount': 100000000000000000000,  # 100 ETH in wei
                'eventId': '0xsuper-bowl-2024',
                'eventTimestamp': 1704067200
            },
            'transactionHash': '0xabc123...',
            'blockNumber': 12345
        }

        Syncs to database by updating the bet record with blockchain fields.
        """
        # Pseudocode:
        #
        # bet_id = event['args']['betId']
        # creator_address = event['args']['creator']
        # stake_amount = self.blockchain_service.web3.fromWei(event['args']['stakeAmount'], 'ether')
        # tx_hash = event['transactionHash'].hex()
        # block_number = event['blockNumber']
        #
        # # Find bet in database by creator address
        # # (Bet was already created via backend API with local ID)
        # user = await self.db.execute(
        #     select(User).where(User.wallet_address == creator_address)
        # )
        # user = user.scalar_one_or_none()
        #
        # if user:
        #     # Find the most recent bet created by this user without blockchain_bet_id
        #     bet = await self.db.execute(
        #         select(Bet)
        #         .where(Bet.creator_id == user.id, Bet.blockchain_bet_id.is_(None))
        #         .order_by(Bet.created_at.desc())
        #         .limit(1)
        #     )
        #     bet = bet.scalar_one_or_none()
        #
        #     if bet:
        #         # Update with blockchain data
        #         bet.blockchain_bet_id = bet_id
        #         bet.blockchain_tx_hash = tx_hash
        #         bet.blockchain_status = 'OPEN'
        #         await self.db.commit()
        #
        #         print(f"[Indexer] BetCreated: bet_id={bet_id}, tx={tx_hash}")

        print(f"[Indexer] BetCreated event: {event}")
        await asyncio.sleep(0.1)

    async def _handle_bet_matched(self, event: Dict[str, Any]):
        """
        Handle BetMatched event.

        Event structure:
        {
            'args': {
                'betId': 42,
                'opponent': '0xBob...',
                'totalEscrow': 200000000000000000000  # 200 ETH in wei
            },
            'transactionHash': '0xdef456...',
            'blockNumber': 12346
        }

        Updates bet status to MATCHED and records opponent.
        """
        # Pseudocode:
        #
        # bet_id = event['args']['betId']
        # opponent_address = event['args']['opponent']
        # total_escrow = self.blockchain_service.web3.fromWei(event['args']['totalEscrow'], 'ether')
        # tx_hash = event['transactionHash'].hex()
        #
        # # Find bet by blockchain_bet_id
        # bet = await self.db.execute(
        #     select(Bet).where(Bet.blockchain_bet_id == bet_id)
        # )
        # bet = bet.scalar_one_or_none()
        #
        # if bet:
        #     # Find opponent user by wallet address
        #     opponent = await self.db.execute(
        #         select(User).where(User.wallet_address == opponent_address)
        #     )
        #     opponent = opponent.scalar_one_or_none()
        #
        #     if opponent:
        #         bet.opponent_id = opponent.id
        #         bet.status = BetStatus.MATCHED
        #         bet.blockchain_status = 'MATCHED'
        #         await self.db.commit()
        #
        #         # Create transaction records for both parties
        #         await create_escrow_lock_transaction(bet.creator_id, bet.stake_amount, bet.id)
        #         await create_escrow_lock_transaction(opponent.id, bet.stake_amount, bet.id)
        #
        #         # Send WebSocket notification
        #         await send_websocket_event("bet_matched", {"bet_id": bet.id})
        #
        #         print(f"[Indexer] BetMatched: bet_id={bet_id}, opponent={opponent_address}")

        print(f"[Indexer] BetMatched event: {event}")
        await asyncio.sleep(0.1)

    async def _handle_bet_settled(self, event: Dict[str, Any]):
        """
        Handle BetSettled event.

        Event structure:
        {
            'args': {
                'betId': 42,
                'winner': '0xAlice...',
                'payoutAmount': 200000000000000000000  # 200 ETH in wei
            },
            'transactionHash': '0xghi789...',
            'blockNumber': 12347
        }

        Updates bet to COMPLETED and records winner.
        """
        # Pseudocode:
        #
        # bet_id = event['args']['betId']
        # winner_address = event['args']['winner']
        # payout_amount = self.blockchain_service.web3.fromWei(event['args']['payoutAmount'], 'ether')
        # tx_hash = event['transactionHash'].hex()
        #
        # # Find bet
        # bet = await self.db.execute(
        #     select(Bet).where(Bet.blockchain_bet_id == bet_id)
        # )
        # bet = bet.scalar_one_or_none()
        #
        # if bet:
        #     # Find winner user
        #     winner = await self.db.execute(
        #         select(User).where(User.wallet_address == winner_address)
        #     )
        #     winner = winner.scalar_one_or_none()
        #
        #     if winner:
        #         bet.winner_id = winner.id
        #         bet.status = BetStatus.COMPLETED
        #         bet.blockchain_status = 'COMPLETED'
        #         bet.settled_at = datetime.utcnow()
        #         await self.db.commit()
        #
        #         # Create transaction records
        #         loser_id = bet.opponent_id if winner.id == bet.creator_id else bet.creator_id
        #
        #         await create_bet_won_transaction(winner.id, payout_amount, bet.id)
        #         await create_bet_lost_transaction(loser_id, bet.stake_amount, bet.id)
        #
        #         # Update user stats
        #         await update_user_stats(winner.id, won=True)
        #         await update_user_stats(loser_id, won=False)
        #
        #         # Send WebSocket notifications
        #         await send_websocket_event("bet_settled", {"bet_id": bet.id, "winner_id": winner.id})
        #
        #         print(f"[Indexer] BetSettled: bet_id={bet_id}, winner={winner_address}")

        print(f"[Indexer] BetSettled event: {event}")
        await asyncio.sleep(0.1)

    async def _handle_bet_disputed(self, event: Dict[str, Any]):
        """
        Handle BetDisputed event.

        Event structure:
        {
            'args': {
                'betId': 42,
                'disputedBy': '0xBob...',
                'timestamp': 1704153600
            },
            'transactionHash': '0xjkl012...',
            'blockNumber': 12348
        }

        Updates bet status to DISPUTED.
        """
        # Pseudocode:
        #
        # bet_id = event['args']['betId']
        # disputed_by_address = event['args']['disputedBy']
        #
        # bet = await self.db.execute(
        #     select(Bet).where(Bet.blockchain_bet_id == bet_id)
        # )
        # bet = bet.scalar_one_or_none()
        #
        # if bet:
        #     bet.status = BetStatus.DISPUTED
        #     bet.blockchain_status = 'DISPUTED'
        #     await self.db.commit()
        #
        #     # Notify admins for manual review
        #     await send_admin_notification("Bet disputed", {"bet_id": bet.id})
        #
        #     print(f"[Indexer] BetDisputed: bet_id={bet_id}, disputed_by={disputed_by_address}")

        print(f"[Indexer] BetDisputed event: {event}")
        await asyncio.sleep(0.1)

    async def reindex_from_block(self, start_block: int):
        """
        Reindex blockchain events from a specific block.

        Useful for recovering from indexer downtime or bugs.

        Args:
            start_block: Block number to start reindexing from
        """
        # Pseudocode:
        #
        # current_block = await self.blockchain_service.web3.eth.block_number
        #
        # print(f"Reindexing from block {start_block} to {current_block}")
        #
        # for block_num in range(start_block, current_block + 1):
        #     await self._index_block(block_num)
        #     if block_num % 100 == 0:
        #         print(f"Progress: {block_num}/{current_block}")
        #
        # print("Reindexing complete")

        print(f"[Indexer] Reindexing from block {start_block}")
        await asyncio.sleep(0.1)


# Singleton instance
_indexer: BlockchainIndexer | None = None


def get_blockchain_indexer(blockchain_service, database_session) -> BlockchainIndexer:
    """Get singleton indexer instance."""
    global _indexer

    if _indexer is None:
        _indexer = BlockchainIndexer(blockchain_service, database_session)

    return _indexer


async def start_indexer_background_task(blockchain_service, database_session):
    """
    Start the indexer as a background task.

    This would be called from main.py on application startup:

    @app.on_event("startup")
    async def startup_event():
        blockchain_service = get_blockchain_service()
        db_session = get_database_session()

        indexer = get_blockchain_indexer(blockchain_service, db_session)
        asyncio.create_task(indexer.start())
    """
    indexer = get_blockchain_indexer(blockchain_service, database_session)
    await indexer.start()
