// Package vault provides HTTP handlers for vault proxy operations.
//
// The gateway acts as a smart proxy between RootWallet clients and
// vault guardian nodes on the WireGuard overlay network. It handles
// Shamir split/combine so clients make a single HTTPS call.
package vault

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/DeBrosOfficial/network/pkg/client"
	"github.com/DeBrosOfficial/network/pkg/logging"
)

const (
	// VaultGuardianPort is the port vault guardians listen on (client API).
	VaultGuardianPort = 7500

	// guardianTimeout is the per-guardian HTTP request timeout.
	guardianTimeout = 5 * time.Second

	// overallTimeout is the maximum time for the full fan-out operation.
	overallTimeout = 15 * time.Second

	// maxPushBodySize limits push request bodies (1 MiB).
	maxPushBodySize = 1 << 20

	// maxPullBodySize limits pull request bodies (4 KiB).
	maxPullBodySize = 4 << 10
)

// Handlers provides HTTP handlers for vault proxy operations.
type Handlers struct {
	logger      *logging.ColoredLogger
	dbClient    client.NetworkClient
	rateLimiter *IdentityRateLimiter
	httpClient  *http.Client
}

// NewHandlers creates vault proxy handlers.
func NewHandlers(logger *logging.ColoredLogger, dbClient client.NetworkClient) *Handlers {
	h := &Handlers{
		logger:   logger,
		dbClient: dbClient,
		rateLimiter: NewIdentityRateLimiter(
			30,  // 30 pushes per hour per identity
			120, // 120 pulls per hour per identity
		),
		httpClient: &http.Client{
			Timeout: guardianTimeout,
			Transport: &http.Transport{
				MaxIdleConns:        100,
				MaxIdleConnsPerHost: 10,
				IdleConnTimeout:     90 * time.Second,
			},
		},
	}
	h.rateLimiter.StartCleanup(10*time.Minute, 1*time.Hour)
	return h
}

// guardian represents a reachable vault guardian node.
type guardian struct {
	IP   string
	Port int
}

// discoverGuardians queries dns_nodes for all active nodes.
// Every Orama node runs a vault guardian, so every active node is a guardian.
func (h *Handlers) discoverGuardians(ctx context.Context) ([]guardian, error) {
	db := h.dbClient.Database()
	internalCtx := client.WithInternalAuth(ctx)

	query := "SELECT COALESCE(internal_ip, ip_address) FROM dns_nodes WHERE status = 'active'"
	result, err := db.Query(internalCtx, query)
	if err != nil {
		return nil, fmt.Errorf("vault: failed to query guardian nodes: %w", err)
	}
	if result == nil || len(result.Rows) == 0 {
		return nil, fmt.Errorf("vault: no active guardian nodes found")
	}

	guardians := make([]guardian, 0, len(result.Rows))
	for _, row := range result.Rows {
		if len(row) == 0 {
			continue
		}
		ip := getString(row[0])
		if ip == "" {
			continue
		}
		guardians = append(guardians, guardian{IP: ip, Port: VaultGuardianPort})
	}
	if len(guardians) == 0 {
		return nil, fmt.Errorf("vault: no guardian nodes with valid IPs found")
	}
	return guardians, nil
}

func writeJSON(w http.ResponseWriter, status int, v interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(v)
}

func writeError(w http.ResponseWriter, status int, msg string) {
	writeJSON(w, status, map[string]string{"error": msg})
}

func getString(v interface{}) string {
	if s, ok := v.(string); ok {
		return s
	}
	return ""
}

// isValidIdentity checks that identity is exactly 64 hex characters.
func isValidIdentity(identity string) bool {
	if len(identity) != 64 {
		return false
	}
	for _, c := range identity {
		if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
			return false
		}
	}
	return true
}
