package rest

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/explorer/backend/api/middleware"
	"github.com/explorer/backend/auth"
	httpmiddleware "github.com/explorer/backend/libs/go-http-middleware"
	"github.com/jackc/pgx/v5/pgxpool"
)

// Server represents the REST API server
type Server struct {
	db         *pgxpool.Pool
	chainID    int
	walletAuth *auth.WalletAuth
	jwtSecret  []byte
	aiLimiter  *AIRateLimiter
	aiMetrics  *AIMetrics
}

// NewServer creates a new REST API server
func NewServer(db *pgxpool.Pool, chainID int) *Server {
	// Get JWT secret from environment or use default
	jwtSecret := []byte(os.Getenv("JWT_SECRET"))
	if len(jwtSecret) == 0 {
		jwtSecret = []byte("change-me-in-production-use-strong-random-secret")
		log.Println("WARNING: Using default JWT secret. Set JWT_SECRET environment variable in production!")
	}

	walletAuth := auth.NewWalletAuth(db, jwtSecret)

	return &Server{
		db:         db,
		chainID:    chainID,
		walletAuth: walletAuth,
		jwtSecret:  jwtSecret,
		aiLimiter:  NewAIRateLimiter(),
		aiMetrics:  NewAIMetrics(),
	}
}

// Start starts the HTTP server
func (s *Server) Start(port int) error {
	mux := http.NewServeMux()
	s.SetupRoutes(mux)

	// Initialize auth middleware
	authMiddleware := middleware.NewAuthMiddleware(s.walletAuth)

	// Setup track routes with proper middleware
	s.SetupTrackRoutes(mux, authMiddleware)

	// Security headers (reusable lib; CSP from env or explorer default)
	csp := os.Getenv("CSP_HEADER")
	if csp == "" {
		csp = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self' https://explorer.d-bis.org https://rpc-http-pub.d-bis.org wss://rpc-ws-pub.d-bis.org http://192.168.11.221:8545 ws://192.168.11.221:8546;"
	}
	securityMiddleware := httpmiddleware.NewSecurity(csp)

	// Add middleware for all routes (outermost to innermost)
	handler := securityMiddleware.AddSecurityHeaders(
		authMiddleware.OptionalAuth( // Optional auth for Track 1, required for others
			s.addMiddleware(
				s.loggingMiddleware(
					s.compressionMiddleware(mux),
				),
			),
		),
	)

	addr := fmt.Sprintf(":%d", port)
	log.Printf("Starting SolaceScanScout REST API server on %s", addr)
	log.Printf("Tiered architecture enabled: Track 1 (public), Track 2-4 (authenticated)")
	return http.ListenAndServe(addr, handler)
}

// addMiddleware adds common middleware to all routes
func (s *Server) addMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Add branding headers
		w.Header().Set("X-Explorer-Name", "SolaceScanScout")
		w.Header().Set("X-Explorer-Version", "1.0.0")
		w.Header().Set("X-Powered-By", "SolaceScanScout")

		// Add CORS headers for API routes (optional: set CORS_ALLOWED_ORIGIN to restrict, e.g. https://explorer.d-bis.org)
		if strings.HasPrefix(r.URL.Path, "/api/") {
			origin := os.Getenv("CORS_ALLOWED_ORIGIN")
			if origin == "" {
				origin = "*"
			}
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
			w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-API-Key")

			// Handle preflight
			if r.Method == "OPTIONS" {
				w.WriteHeader(http.StatusOK)
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}

// requireDB returns false and writes 503 if db is nil (e.g. in tests without DB)
func (s *Server) requireDB(w http.ResponseWriter) bool {
	if s.db == nil {
		writeError(w, http.StatusServiceUnavailable, "service_unavailable", "database unavailable")
		return false
	}
	return true
}

// handleListBlocks handles GET /api/v1/blocks
func (s *Server) handleListBlocks(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 chain_id, number, hash, timestamp, timestamp_iso, miner, transaction_count, gas_used, gas_limit
		FROM blocks
		WHERE chain_id = $1
		ORDER BY number DESC
		LIMIT $2 OFFSET $3
	`

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

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

	blocks := []map[string]interface{}{}
	for rows.Next() {
		var chainID, number, transactionCount int
		var hash, miner string
		var timestamp time.Time
		var timestampISO sql.NullString
		var gasUsed, gasLimit int64

		if err := rows.Scan(&chainID, &number, &hash, &timestamp, &timestampISO, &miner, &transactionCount, &gasUsed, &gasLimit); err != nil {
			continue
		}

		block := map[string]interface{}{
			"chain_id":          chainID,
			"number":            number,
			"hash":              hash,
			"timestamp":         timestamp,
			"miner":             miner,
			"transaction_count": transactionCount,
			"gas_used":          gasUsed,
			"gas_limit":         gasLimit,
		}

		if timestampISO.Valid {
			block["timestamp_iso"] = timestampISO.String
		}

		blocks = append(blocks, block)
	}

	response := map[string]interface{}{
		"data": blocks,
		"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)
}

// handleGetBlock, handleListTransactions, handleGetTransaction, handleGetAddress
// are implemented in blocks.go, transactions.go, and addresses.go respectively

// handleHealth handles GET /health
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("X-Explorer-Name", "SolaceScanScout")
	w.Header().Set("X-Explorer-Version", "1.0.0")

	// Check database connection
	dbStatus := "ok"
	if s.db != nil {
		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancel()
		if err := s.db.Ping(ctx); err != nil {
			dbStatus = "error: " + err.Error()
		}
	} else {
		dbStatus = "unavailable"
	}

	health := map[string]interface{}{
		"status":    "healthy",
		"timestamp": time.Now().UTC().Format(time.RFC3339),
		"services": map[string]string{
			"database": dbStatus,
			"api":      "ok",
		},
		"chain_id": s.chainID,
		"explorer": map[string]string{
			"name":    "SolaceScanScout",
			"version": "1.0.0",
		},
	}

	statusCode := http.StatusOK
	if dbStatus != "ok" {
		statusCode = http.StatusServiceUnavailable
		health["status"] = "degraded"
	}

	w.WriteHeader(statusCode)
	json.NewEncoder(w).Encode(health)
}
