package rest

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

// handleListTransactions handles GET /api/v1/transactions
func (s *Server) handleListTransactions(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeMethodNotAllowed(w)
		return
	}
	if !s.requireDB(w) {
		return
	}

	// Validate pagination
	page, pageSize, err := validatePagination(
		r.URL.Query().Get("page"),
		r.URL.Query().Get("page_size"),
	)
	if err != nil {
		writeValidationError(w, err)
		return
	}

	offset := (page - 1) * pageSize

	query := `
		SELECT t.chain_id, t.hash, t.block_number, t.transaction_index, t.from_address, t.to_address,
		       t.value, t.gas_price, t.gas_used, t.status, t.created_at, t.timestamp_iso
		FROM transactions t
		WHERE t.chain_id = $1
	`

	args := []interface{}{s.chainID}
	argIndex := 2

	// Add filters
	if blockNumber := r.URL.Query().Get("block_number"); blockNumber != "" {
		if bn, err := strconv.ParseInt(blockNumber, 10, 64); err == nil {
			query += fmt.Sprintf(" AND block_number = $%d", argIndex)
			args = append(args, bn)
			argIndex++
		}
	}

	if fromAddress := r.URL.Query().Get("from_address"); fromAddress != "" {
		query += fmt.Sprintf(" AND from_address = $%d", argIndex)
		args = append(args, fromAddress)
		argIndex++
	}

	if toAddress := r.URL.Query().Get("to_address"); toAddress != "" {
		query += fmt.Sprintf(" AND to_address = $%d", argIndex)
		args = append(args, toAddress)
		argIndex++
	}

	query += " ORDER BY block_number DESC, transaction_index DESC"
	query += fmt.Sprintf(" LIMIT $%d OFFSET $%d", argIndex, argIndex+1)
	args = append(args, pageSize, offset)

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

	rows, err := s.db.Query(ctx, query, args...)
	if err != nil {
		writeInternalError(w, "Database error")
		return
	}
	defer rows.Close()

	transactions := []map[string]interface{}{}
	for rows.Next() {
		var chainID, blockNumber, transactionIndex int
		var hash, fromAddress string
		var toAddress sql.NullString
		var value string
		var gasPrice, gasUsed sql.NullInt64
		var status sql.NullInt64
		var createdAt time.Time
		var timestampISO sql.NullString

		if err := rows.Scan(&chainID, &hash, &blockNumber, &transactionIndex, &fromAddress, &toAddress,
			&value, &gasPrice, &gasUsed, &status, &createdAt, &timestampISO); err != nil {
			continue
		}

		tx := map[string]interface{}{
			"chain_id":          chainID,
			"hash":              hash,
			"block_number":      blockNumber,
			"transaction_index": transactionIndex,
			"from_address":      fromAddress,
			"value":             value,
			"created_at":        createdAt,
		}

		if timestampISO.Valid {
			tx["timestamp_iso"] = timestampISO.String
		}
		if toAddress.Valid {
			tx["to_address"] = toAddress.String
		}
		if gasPrice.Valid {
			tx["gas_price"] = gasPrice.Int64
		}
		if gasUsed.Valid {
			tx["gas_used"] = gasUsed.Int64
		}
		if status.Valid {
			tx["status"] = status.Int64
		}

		transactions = append(transactions, tx)
	}

	response := map[string]interface{}{
		"data": transactions,
		"meta": map[string]interface{}{
			"pagination": map[string]interface{}{
				"page":      page,
				"page_size": pageSize,
			},
		},
	}

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

// handleGetTransactionByHash handles GET /api/v1/transactions/{chain_id}/{hash}
func (s *Server) handleGetTransactionByHash(w http.ResponseWriter, r *http.Request, hash string) {
	// Validate hash format (already validated in routes.go, but double-check)
	if !isValidHash(hash) {
		writeValidationError(w, ErrInvalidHash)
		return
	}

	// Add query timeout
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	query := `
		SELECT chain_id, hash, block_number, block_hash, transaction_index,
		       from_address, to_address, value, gas_price, max_fee_per_gas,
		       max_priority_fee_per_gas, gas_limit, gas_used, nonce, input_data,
		       status, contract_address, cumulative_gas_used, effective_gas_price,
		       created_at, timestamp_iso
		FROM transactions
		WHERE chain_id = $1 AND hash = $2
	`

	var chainID, blockNumber, transactionIndex int
	var txHash, blockHash, fromAddress string
	var toAddress sql.NullString
	var value string
	var gasPrice, maxFeePerGas, maxPriorityFeePerGas, gasLimit, gasUsed, nonce sql.NullInt64
	var inputData sql.NullString
	var status sql.NullInt64
	var contractAddress sql.NullString
	var cumulativeGasUsed int64
	var effectiveGasPrice sql.NullInt64
	var createdAt time.Time
	var timestampISO sql.NullString

	err := s.db.QueryRow(ctx, query, s.chainID, hash).Scan(
		&chainID, &txHash, &blockNumber, &blockHash, &transactionIndex,
		&fromAddress, &toAddress, &value, &gasPrice, &maxFeePerGas,
		&maxPriorityFeePerGas, &gasLimit, &gasUsed, &nonce, &inputData,
		&status, &contractAddress, &cumulativeGasUsed, &effectiveGasPrice,
		&createdAt, &timestampISO,
	)

	if err != nil {
		writeNotFound(w, "Transaction")
		return
	}

	tx := map[string]interface{}{
		"chain_id":            chainID,
		"hash":                txHash,
		"block_number":        blockNumber,
		"block_hash":          blockHash,
		"transaction_index":   transactionIndex,
		"from_address":        fromAddress,
		"value":               value,
		"gas_limit":           gasLimit.Int64,
		"cumulative_gas_used": cumulativeGasUsed,
		"created_at":          createdAt,
	}

	if timestampISO.Valid {
		tx["timestamp_iso"] = timestampISO.String
	}
	if toAddress.Valid {
		tx["to_address"] = toAddress.String
	}
	if gasPrice.Valid {
		tx["gas_price"] = gasPrice.Int64
	}
	if maxFeePerGas.Valid {
		tx["max_fee_per_gas"] = maxFeePerGas.Int64
	}
	if maxPriorityFeePerGas.Valid {
		tx["max_priority_fee_per_gas"] = maxPriorityFeePerGas.Int64
	}
	if gasUsed.Valid {
		tx["gas_used"] = gasUsed.Int64
	}
	if nonce.Valid {
		tx["nonce"] = nonce.Int64
	}
	if inputData.Valid {
		tx["input_data"] = inputData.String
	}
	if status.Valid {
		tx["status"] = status.Int64
	}
	if contractAddress.Valid {
		tx["contract_address"] = contractAddress.String
	}
	if effectiveGasPrice.Valid {
		tx["effective_gas_price"] = effectiveGasPrice.Int64
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"data": tx,
	})
}
