package indexer

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/elastic/go-elasticsearch/v8"
	"github.com/elastic/go-elasticsearch/v8/esapi"
)

// SearchIndexer handles indexing documents to Elasticsearch/OpenSearch
type SearchIndexer struct {
	client      *elasticsearch.Client
	indexPrefix string
}

// NewSearchIndexer creates a new search indexer
func NewSearchIndexer(client *elasticsearch.Client, indexPrefix string) *SearchIndexer {
	return &SearchIndexer{
		client:      client,
		indexPrefix: indexPrefix,
	}
}

// IndexBlock indexes a block document
func (s *SearchIndexer) IndexBlock(ctx context.Context, chainID int, block *BlockDocument) error {
	indexName := fmt.Sprintf("%s-blocks-%d", s.indexPrefix, chainID)
	return s.indexDocument(ctx, indexName, block.Hash, block)
}

// IndexTransaction indexes a transaction document
func (s *SearchIndexer) IndexTransaction(ctx context.Context, chainID int, tx *TransactionDocument) error {
	indexName := fmt.Sprintf("%s-transactions-%d", s.indexPrefix, chainID)
	return s.indexDocument(ctx, indexName, tx.Hash, tx)
}

// IndexAddress indexes an address document
func (s *SearchIndexer) IndexAddress(ctx context.Context, chainID int, addr *AddressDocument) error {
	indexName := fmt.Sprintf("%s-addresses-%d", s.indexPrefix, chainID)
	return s.indexDocument(ctx, indexName, addr.Address, addr)
}

// indexDocument indexes a document to Elasticsearch
func (s *SearchIndexer) indexDocument(ctx context.Context, indexName, docID string, doc interface{}) error {
	body, err := json.Marshal(doc)
	if err != nil {
		return fmt.Errorf("failed to marshal document: %w", err)
	}

	req := esapi.IndexRequest{
		Index:      indexName,
		DocumentID: docID,
		Body:       bytes.NewReader(body),
		Refresh:    "false",
	}

	res, err := req.Do(ctx, s.client)
	if err != nil {
		return fmt.Errorf("failed to index document: %w", err)
	}
	defer res.Body.Close()

	if res.IsError() {
		return fmt.Errorf("elasticsearch error: %s", res.String())
	}

	return nil
}

// BlockDocument represents a block in the search index
type BlockDocument struct {
	BlockNumber      int64     `json:"block_number"`
	Hash             string    `json:"hash"`
	Timestamp        time.Time `json:"timestamp"`
	Miner            string    `json:"miner"`
	TransactionCount int       `json:"transaction_count"`
	GasUsed          int64     `json:"gas_used"`
	GasLimit         int64     `json:"gas_limit"`
	ChainID          int       `json:"chain_id"`
	ParentHash       string    `json:"parent_hash"`
	Size             int64     `json:"size"`
}

// TransactionDocument represents a transaction in the search index
type TransactionDocument struct {
	Hash               string    `json:"hash"`
	BlockNumber        int64     `json:"block_number"`
	TransactionIndex   int       `json:"transaction_index"`
	FromAddress        string    `json:"from_address"`
	ToAddress          string    `json:"to_address"`
	Value              string    `json:"value"`
	ValueNumeric       int64     `json:"value_numeric"`
	GasPrice           int64     `json:"gas_price"`
	GasUsed            int64     `json:"gas_used"`
	Status             string    `json:"status"`
	Timestamp          time.Time `json:"timestamp"`
	ChainID            int       `json:"chain_id"`
	InputDataLength    int       `json:"input_data_length"`
	IsContractCreation bool      `json:"is_contract_creation"`
	ContractAddress    string    `json:"contract_address,omitempty"`
}

// AddressDocument represents an address in the search index
type AddressDocument struct {
	Address          string    `json:"address"`
	ChainID          int       `json:"chain_id"`
	Label            string    `json:"label,omitempty"`
	Tags             []string  `json:"tags,omitempty"`
	TokenCount       int       `json:"token_count"`
	TransactionCount int64     `json:"transaction_count"`
	FirstSeen        time.Time `json:"first_seen"`
	LastSeen         time.Time `json:"last_seen"`
	IsContract       bool      `json:"is_contract"`
	ContractName     string    `json:"contract_name,omitempty"`
	BalanceETH       string    `json:"balance_eth"`
	BalanceUSD       float64   `json:"balance_usd,omitempty"`
}
