package rest

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"math/big"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// handleEtherscanAPI handles GET /api?module=...&action=...
// This provides Etherscan-compatible API endpoints
func (s *Server) handleEtherscanAPI(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeMethodNotAllowed(w)
		return
	}
	if !s.requireDB(w) {
		return
	}

	module := r.URL.Query().Get("module")
	action := r.URL.Query().Get("action")

	// Etherscan-compatible response structure
	type EtherscanResponse struct {
		Status  string      `json:"status"`
		Message string      `json:"message"`
		Result  interface{} `json:"result"`
	}

	// Validate required parameters
	if module == "" || action == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		response := EtherscanResponse{
			Status:  "0",
			Message: "Params 'module' and 'action' are required parameters",
			Result:  nil,
		}
		json.NewEncoder(w).Encode(response)
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	var response EtherscanResponse

	switch module {
	case "block":
		switch action {
		case "eth_block_number":
			// Get latest block number
			var blockNumber int64
			err := s.db.QueryRow(ctx,
				`SELECT MAX(number) FROM blocks WHERE chain_id = $1`,
				s.chainID,
			).Scan(&blockNumber)
			if err != nil {
				response = EtherscanResponse{
					Status:  "0",
					Message: "Error",
					Result:  "0x0",
				}
			} else {
				response = EtherscanResponse{
					Status:  "1",
					Message: "OK",
					Result:  fmt.Sprintf("0x%x", blockNumber),
				}
			}

		case "eth_get_block_by_number":
			tag := r.URL.Query().Get("tag")
			boolean := r.URL.Query().Get("boolean") == "true"

			// Parse block number from tag (can be "latest", "0x...", or decimal)
			var blockNumber int64
			if tag == "latest" {
				err := s.db.QueryRow(ctx,
					`SELECT MAX(number) FROM blocks WHERE chain_id = $1`,
					s.chainID,
				).Scan(&blockNumber)
				if err != nil {
					response = EtherscanResponse{
						Status:  "0",
						Message: "Error",
						Result:  nil,
					}
					break
				}
			} else if len(tag) > 2 && tag[:2] == "0x" {
				// Hex format
				parsed, err := strconv.ParseInt(tag[2:], 16, 64)
				if err != nil {
					response = EtherscanResponse{
						Status:  "0",
						Message: "Invalid block number",
						Result:  nil,
					}
					break
				}
				blockNumber = parsed
			} else {
				// Decimal format
				parsed, err := strconv.ParseInt(tag, 10, 64)
				if err != nil {
					response = EtherscanResponse{
						Status:  "0",
						Message: "Invalid block number",
						Result:  nil,
					}
					break
				}
				blockNumber = parsed
			}

			// Get block data
			var hash, parentHash, miner string
			var timestamp time.Time
			var transactionCount int
			var gasUsed, gasLimit int64
			var transactions interface{}

			query := `
				SELECT hash, parent_hash, timestamp, miner, transaction_count, gas_used, gas_limit
				FROM blocks
				WHERE chain_id = $1 AND number = $2
			`

			err := s.db.QueryRow(ctx, query, s.chainID, blockNumber).Scan(
				&hash, &parentHash, &timestamp, &miner, &transactionCount, &gasUsed, &gasLimit,
			)
			if err != nil {
				response = EtherscanResponse{
					Status:  "0",
					Message: "Block not found",
					Result:  nil,
				}
				break
			}

			if boolean {
				txObjects, err := s.loadEtherscanBlockTransactions(ctx, blockNumber)
				if err != nil {
					response = EtherscanResponse{
						Status:  "0",
						Message: "Error",
						Result:  nil,
					}
					break
				}
				transactions = txObjects
			} else {
				txHashes, err := s.loadEtherscanBlockTransactionHashes(ctx, blockNumber)
				if err != nil {
					response = EtherscanResponse{
						Status:  "0",
						Message: "Error",
						Result:  nil,
					}
					break
				}
				transactions = txHashes
			}

			blockResult := map[string]interface{}{
				"number":           fmt.Sprintf("0x%x", blockNumber),
				"hash":             hash,
				"parentHash":       parentHash,
				"timestamp":        fmt.Sprintf("0x%x", timestamp.Unix()),
				"miner":            miner,
				"transactions":     transactions,
				"transactionCount": fmt.Sprintf("0x%x", transactionCount),
				"gasUsed":          fmt.Sprintf("0x%x", gasUsed),
				"gasLimit":         fmt.Sprintf("0x%x", gasLimit),
			}

			response = EtherscanResponse{
				Status:  "1",
				Message: "OK",
				Result:  blockResult,
			}

		default:
			response = EtherscanResponse{
				Status:  "0",
				Message: "Invalid action",
				Result:  nil,
			}
		}

	default:
		response = EtherscanResponse{
			Status:  "0",
			Message: "Invalid module",
			Result:  nil,
		}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func (s *Server) loadEtherscanBlockTransactionHashes(ctx context.Context, blockNumber int64) ([]string, error) {
	rows, err := s.db.Query(ctx, `
		SELECT hash
		FROM transactions
		WHERE chain_id = $1 AND block_number = $2
		ORDER BY transaction_index
	`, s.chainID, blockNumber)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	hashes := make([]string, 0)
	for rows.Next() {
		var txHash string
		if err := rows.Scan(&txHash); err != nil {
			return nil, err
		}
		hashes = append(hashes, txHash)
	}

	return hashes, rows.Err()
}

func (s *Server) loadEtherscanBlockTransactions(ctx context.Context, blockNumber int64) ([]map[string]interface{}, error) {
	rows, err := s.db.Query(ctx, `
		SELECT hash, block_hash, transaction_index, from_address, to_address, value::text,
		       COALESCE(gas_price, 0), gas_limit, nonce, COALESCE(input_data, '')
		FROM transactions
		WHERE chain_id = $1 AND block_number = $2
		ORDER BY transaction_index
	`, s.chainID, blockNumber)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	transactions := make([]map[string]interface{}, 0)
	for rows.Next() {
		var hash, blockHash, fromAddress, value, inputData string
		var toAddress sql.NullString
		var transactionIndex int
		var gasPrice, gasLimit, nonce int64
		if err := rows.Scan(&hash, &blockHash, &transactionIndex, &fromAddress, &toAddress, &value, &gasPrice, &gasLimit, &nonce, &inputData); err != nil {
			return nil, err
		}

		tx := map[string]interface{}{
			"hash":             hash,
			"blockHash":        blockHash,
			"blockNumber":      fmt.Sprintf("0x%x", blockNumber),
			"transactionIndex": fmt.Sprintf("0x%x", transactionIndex),
			"from":             fromAddress,
			"value":            decimalStringToHex(value),
			"gasPrice":         fmt.Sprintf("0x%x", gasPrice),
			"gas":              fmt.Sprintf("0x%x", gasLimit),
			"nonce":            fmt.Sprintf("0x%x", nonce),
			"input":            normalizeHexInput(inputData),
		}
		if toAddress.Valid && toAddress.String != "" {
			tx["to"] = toAddress.String
		} else {
			tx["to"] = nil
		}

		transactions = append(transactions, tx)
	}

	return transactions, rows.Err()
}

func decimalStringToHex(value string) string {
	parsed, ok := new(big.Int).SetString(value, 10)
	if !ok {
		return "0x0"
	}
	return "0x" + parsed.Text(16)
}

func normalizeHexInput(input string) string {
	trimmed := strings.TrimSpace(input)
	if trimmed == "" {
		return "0x"
	}
	if strings.HasPrefix(trimmed, "0x") {
		return trimmed
	}
	return "0x" + trimmed
}
