package genex

import (
	"context"
	"time"

	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/core/types"
)

// SubscribeEvents 监听链上事件
func (c *Client) SubscribeEvents(ctx context.Context, filter EventFilter) (<-chan ChainEvent, error) {
	ch := make(chan ChainEvent, 100)

	query := ethereum.FilterQuery{}
	if filter.ContractAddress != nil {
		query.Addresses = append(query.Addresses, *filter.ContractAddress)
	}
	if filter.FromBlock != nil {
		query.FromBlock = filter.FromBlock
	}
	if filter.ToBlock != nil {
		query.ToBlock = filter.ToBlock
	}

	logCh := make(chan types.Log, 100)
	sub, err := c.ethClient.SubscribeFilterLogs(ctx, query, logCh)
	if err != nil {
		return nil, err
	}

	go func() {
		defer close(ch)
		for {
			select {
			case log := <-logCh:
				ch <- ChainEvent{
					Type:        "log",
					BlockHeight: int64(log.BlockNumber),
					TxHash:      log.TxHash.Hex(),
					Data: map[string]interface{}{
						"address": log.Address.Hex(),
						"topics":  log.Topics,
						"data":    log.Data,
					},
				}
			case err := <-sub.Err():
				if err != nil {
					return
				}
			case <-ctx.Done():
				sub.Unsubscribe()
				return
			}
		}
	}()

	return ch, nil
}

// SubscribeNewBlocks 监听新区块
func (c *Client) SubscribeNewBlocks(ctx context.Context) (<-chan *BlockInfo, error) {
	ch := make(chan *BlockInfo, 10)

	headers := make(chan *types.Header, 10)
	sub, err := c.ethClient.SubscribeNewHead(ctx, headers)
	if err != nil {
		return nil, err
	}

	go func() {
		defer close(ch)
		for {
			select {
			case header := <-headers:
				ch <- &BlockInfo{
					Height:    header.Number.Int64(),
					Hash:      header.Hash().Hex(),
					Timestamp: time.Unix(int64(header.Time), 0),
					Proposer:  header.Coinbase.Hex(),
				}
			case <-sub.Err():
				return
			case <-ctx.Done():
				sub.Unsubscribe()
				return
			}
		}
	}()

	return ch, nil
}
