package track1

import (
	"encoding/json"
	"fmt"
	"math/big"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/explorer/backend/libs/go-rpc-gateway"
)

// Server handles Track 1 endpoints (uses RPC gateway from lib)
type Server struct {
	rpcGateway *gateway.RPCGateway
}

// NewServer creates a new Track 1 server
func NewServer(rpcGateway *gateway.RPCGateway) *Server {
	return &Server{
		rpcGateway: rpcGateway,
	}
}

// HandleLatestBlocks handles GET /api/v1/track1/blocks/latest
func (s *Server) HandleLatestBlocks(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	limit := 10
	if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
		if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 50 {
			limit = l
		}
	}

	// Get latest block number
	blockNumResp, err := s.rpcGateway.GetBlockNumber(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, "rpc_error", err.Error())
		return
	}

	blockNumHex, ok := blockNumResp.Result.(string)
	if !ok {
		writeError(w, http.StatusInternalServerError, "invalid_response", "Invalid block number response")
		return
	}

	// Parse block number
	blockNum, err := hexToInt(blockNumHex)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "parse_error", err.Error())
		return
	}

	// Fetch blocks
	blocks := []map[string]interface{}{}
	for i := 0; i < limit && blockNum-int64(i) >= 0; i++ {
		blockNumStr := fmt.Sprintf("0x%x", blockNum-int64(i))
		blockResp, err := s.rpcGateway.GetBlockByNumber(r.Context(), blockNumStr, false)
		if err != nil {
			continue // Skip failed blocks
		}

		blockData, ok := blockResp.Result.(map[string]interface{})
		if !ok {
			continue
		}

		// Transform to our format
		block := transformBlock(blockData)
		blocks = append(blocks, block)
	}

	response := map[string]interface{}{
		"data": blocks,
		"pagination": map[string]interface{}{
			"page":  1,
			"limit": limit,
		},
	}

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

// HandleLatestTransactions handles GET /api/v1/track1/txs/latest
func (s *Server) HandleLatestTransactions(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	limit := 10
	if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
		if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 50 {
			limit = l
		}
	}

	// Get latest block number
	blockNumResp, err := s.rpcGateway.GetBlockNumber(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, "rpc_error", err.Error())
		return
	}

	blockNumHex, ok := blockNumResp.Result.(string)
	if !ok {
		writeError(w, http.StatusInternalServerError, "invalid_response", "Invalid block number response")
		return
	}

	blockNum, err := hexToInt(blockNumHex)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "parse_error", err.Error())
		return
	}

	// Fetch transactions from recent blocks
	transactions := []map[string]interface{}{}
	for i := 0; i < 20 && len(transactions) < limit && blockNum-int64(i) >= 0; i++ {
		blockNumStr := fmt.Sprintf("0x%x", blockNum-int64(i))
		blockResp, err := s.rpcGateway.GetBlockByNumber(r.Context(), blockNumStr, true)
		if err != nil {
			continue
		}

		blockData, ok := blockResp.Result.(map[string]interface{})
		if !ok {
			continue
		}

		txs, ok := blockData["transactions"].([]interface{})
		if !ok {
			continue
		}

		for _, tx := range txs {
			if len(transactions) >= limit {
				break
			}
			txData, ok := tx.(map[string]interface{})
			if !ok {
				continue
			}
			transactions = append(transactions, transformTransaction(txData))
		}
	}

	response := map[string]interface{}{
		"data": transactions,
		"pagination": map[string]interface{}{
			"page":  1,
			"limit": limit,
		},
	}

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

// HandleBlockDetail handles GET /api/v1/track1/block/:number
func (s *Server) HandleBlockDetail(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	path := strings.TrimPrefix(r.URL.Path, "/api/v1/track1/block/")
	blockNumStr := fmt.Sprintf("0x%x", parseBlockNumber(path))

	blockResp, err := s.rpcGateway.GetBlockByNumber(r.Context(), blockNumStr, false)
	if err != nil {
		writeError(w, http.StatusNotFound, "not_found", "Block not found")
		return
	}

	blockData, ok := blockResp.Result.(map[string]interface{})
	if !ok {
		writeError(w, http.StatusInternalServerError, "invalid_response", "Invalid block response")
		return
	}

	response := map[string]interface{}{
		"data": transformBlock(blockData),
	}

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

// HandleTransactionDetail handles GET /api/v1/track1/tx/:hash
func (s *Server) HandleTransactionDetail(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	path := strings.TrimPrefix(r.URL.Path, "/api/v1/track1/tx/")
	txHash := path

	txResp, err := s.rpcGateway.GetTransactionByHash(r.Context(), txHash)
	if err != nil {
		writeError(w, http.StatusNotFound, "not_found", "Transaction not found")
		return
	}

	txData, ok := txResp.Result.(map[string]interface{})
	if !ok {
		writeError(w, http.StatusInternalServerError, "invalid_response", "Invalid transaction response")
		return
	}

	response := map[string]interface{}{
		"data": transformTransaction(txData),
	}

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

// HandleAddressBalance handles GET /api/v1/track1/address/:addr/balance
func (s *Server) HandleAddressBalance(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	path := strings.TrimPrefix(r.URL.Path, "/api/v1/track1/address/")
	parts := strings.Split(path, "/")
	if len(parts) < 2 || parts[1] != "balance" {
		writeError(w, http.StatusBadRequest, "bad_request", "Invalid path")
		return
	}

	address := parts[0]
	balanceResp, err := s.rpcGateway.GetBalance(r.Context(), address, "latest")
	if err != nil {
		writeError(w, http.StatusInternalServerError, "rpc_error", err.Error())
		return
	}

	balanceHex, ok := balanceResp.Result.(string)
	if !ok {
		writeError(w, http.StatusInternalServerError, "invalid_response", "Invalid balance response")
		return
	}

	balance, err := hexToBigInt(balanceHex)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "parse_error", err.Error())
		return
	}

	response := map[string]interface{}{
		"data": map[string]interface{}{
			"address":       address,
			"balance":       balance.String(),
			"balance_wei":   balance.String(),
			"balance_ether": weiToEther(balance),
		},
	}

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

// HandleBridgeStatus handles GET /api/v1/track1/bridge/status
func (s *Server) HandleBridgeStatus(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}

	// Return bridge status (simplified - in production, query bridge contracts)
	response := map[string]interface{}{
		"data": map[string]interface{}{
			"status": "operational",
			"chains": map[string]interface{}{
				"138": map[string]interface{}{
					"name":      "Defi Oracle Meta Mainnet",
					"status":    "operational",
					"last_sync": time.Now().UTC().Format(time.RFC3339),
				},
				"1": map[string]interface{}{
					"name":      "Ethereum Mainnet",
					"status":    "operational",
					"last_sync": time.Now().UTC().Format(time.RFC3339),
				},
			},
			"total_transfers_24h": 150,
			"total_volume_24h":     "5000000000000000000000",
		},
	}

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

// Helper functions
func writeError(w http.ResponseWriter, statusCode int, code, message string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(statusCode)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"error": map[string]interface{}{
			"code":    code,
			"message": message,
		},
	})
}

func hexToInt(hex string) (int64, error) {
	hex = strings.TrimPrefix(hex, "0x")
	return strconv.ParseInt(hex, 16, 64)
}

func parseBlockNumber(s string) int64 {
	num, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func transformBlock(blockData map[string]interface{}) map[string]interface{} {
	return map[string]interface{}{
		"number":            parseHexField(blockData["number"]),
		"hash":              blockData["hash"],
		"parent_hash":       blockData["parentHash"],
		"timestamp":         parseHexTimestamp(blockData["timestamp"]),
		"transaction_count": countTransactions(blockData["transactions"]),
		"gas_used":          parseHexField(blockData["gasUsed"]),
		"gas_limit":         parseHexField(blockData["gasLimit"]),
		"miner":             blockData["miner"],
	}
}

func transformTransaction(txData map[string]interface{}) map[string]interface{} {
	return map[string]interface{}{
		"hash":         txData["hash"],
		"from":         txData["from"],
		"to":           txData["to"],
		"value":        txData["value"],
		"block_number": parseHexField(txData["blockNumber"]),
		"timestamp":    parseHexTimestamp(txData["timestamp"]),
	}
}

func parseHexField(field interface{}) interface{} {
	if str, ok := field.(string); ok {
		if num, err := hexToInt(str); err == nil {
			return num
		}
	}
	return field
}

func parseHexTimestamp(field interface{}) string {
	if str, ok := field.(string); ok {
		if num, err := hexToInt(str); err == nil {
			return time.Unix(num, 0).Format(time.RFC3339)
		}
	}
	return ""
}

func countTransactions(txs interface{}) int {
	if txsList, ok := txs.([]interface{}); ok {
		return len(txsList)
	}
	return 0
}

func hexToBigInt(hex string) (*big.Int, error) {
	hex = strings.TrimPrefix(hex, "0x")
	bigInt := new(big.Int)
	bigInt, ok := bigInt.SetString(hex, 16)
	if !ok {
		return nil, fmt.Errorf("invalid hex number")
	}
	return bigInt, nil
}

func weiToEther(wei *big.Int) string {
	ether := new(big.Float).Quo(new(big.Float).SetInt(wei), big.NewFloat(1e18))
	return ether.Text('f', 18)
}
