package client

import (
	"os"
	"strconv"
	"strings"

	"github.com/DeBrosOfficial/network/pkg/config"
	"github.com/multiformats/go-multiaddr"
)

// DefaultBootstrapPeers returns the default peer multiaddrs.
// These can be overridden by environment variables or config.
func DefaultBootstrapPeers() []string {
	// Check environment variable first
	if envPeers := os.Getenv("DEBROS_BOOTSTRAP_PEERS"); envPeers != "" {
		peers := splitCSVOrSpace(envPeers)
		// Filter out empty strings
		result := make([]string, 0, len(peers))
		for _, p := range peers {
			if p != "" {
				result = append(result, p)
			}
		}
		if len(result) > 0 {
			return result
		}
	}

	defaultCfg := config.DefaultConfig()
	return defaultCfg.Discovery.BootstrapPeers
}

// DefaultDatabaseEndpoints returns default DB HTTP endpoints.
// These can be overridden by environment variables or config.
func DefaultDatabaseEndpoints() []string {
	// Check environment variable first
	if envNodes := os.Getenv("RQLITE_NODES"); envNodes != "" {
		return normalizeEndpoints(splitCSVOrSpace(envNodes))
	}

	// Get default port from environment or use port from config
	defaultCfg := config.DefaultConfig()
	port := defaultCfg.Database.RQLitePort
	if envPort := os.Getenv("RQLITE_PORT"); envPort != "" {
		if p, err := strconv.Atoi(envPort); err == nil && p > 0 {
			port = p
		}
	}

	// Try to derive from configured peers if available
	peers := DefaultBootstrapPeers()
	if len(peers) > 0 {
		endpoints := make([]string, 0, len(peers))
		for _, s := range peers {
			ma, err := multiaddr.NewMultiaddr(s)
			if err != nil {
				continue
			}
			endpoints = append(endpoints, endpointFromMultiaddr(ma, port))
		}
		return dedupeStrings(endpoints)
	}

	// Fallback to localhost
	return []string{"http://localhost:" + strconv.Itoa(port)}
}

// MapAddrsToDBEndpoints converts a set of peer multiaddrs to DB HTTP endpoints using dbPort.
func MapAddrsToDBEndpoints(addrs []multiaddr.Multiaddr, dbPort int) []string {
	if dbPort <= 0 {
		dbPort = 5001
	}
	eps := make([]string, 0, len(addrs))
	for _, ma := range addrs {
		eps = append(eps, endpointFromMultiaddr(ma, dbPort))
	}
	return dedupeStrings(eps)
}

// endpointFromMultiaddr extracts host from multiaddr and creates HTTP endpoint
func endpointFromMultiaddr(ma multiaddr.Multiaddr, port int) string {
	var host string

	// Prefer DNS if present, then IP
	if v, err := ma.ValueForProtocol(multiaddr.P_DNS); err == nil && v != "" {
		host = v
	}
	if host == "" {
		if v, err := ma.ValueForProtocol(multiaddr.P_DNS4); err == nil && v != "" {
			host = v
		}
	}
	if host == "" {
		if v, err := ma.ValueForProtocol(multiaddr.P_DNS6); err == nil && v != "" {
			host = v
		}
	}
	if host == "" {
		if v, err := ma.ValueForProtocol(multiaddr.P_IP4); err == nil && v != "" {
			host = v
		}
	}
	if host == "" {
		if v, err := ma.ValueForProtocol(multiaddr.P_IP6); err == nil && v != "" {
			host = "[" + v + "]" // IPv6 needs brackets in URLs
		}
	}
	if host == "" {
		host = "localhost"
	}

	return "http://" + host + ":" + strconv.Itoa(port)
}

// normalizeEndpoints ensures each endpoint has an http scheme and a port (defaults to 5001)
func normalizeEndpoints(in []string) []string {
	out := make([]string, 0, len(in))
	for _, s := range in {
		s = strings.TrimSpace(s)
		if s == "" {
			continue
		}

		// Prepend scheme if missing
		if !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") {
			s = "http://" + s
		}

		// Simple check for port (doesn't handle all cases but good enough)
		if !strings.Contains(s, ":5001") && !strings.Contains(s, ":500") && !strings.Contains(s, ":501") {
			// Check if there's already a port after the host
			parts := strings.Split(s, "://")
			if len(parts) == 2 {
				hostPart := parts[1]
				// Count colons to detect port (simple heuristic)
				colonCount := strings.Count(hostPart, ":")
				if colonCount == 0 || (strings.Contains(hostPart, "[") && colonCount == 1) {
					// No port found, add default
					s = s + ":5001"
				}
			}
		}

		out = append(out, s)
	}
	return out
}

// dedupeStrings removes duplicate strings from slice
func dedupeStrings(in []string) []string {
	if len(in) == 0 {
		return in
	}

	seen := make(map[string]struct{}, len(in))
	out := make([]string, 0, len(in))

	for _, s := range in {
		s = strings.TrimSpace(s)
		if s == "" {
			continue
		}
		if _, ok := seen[s]; ok {
			continue
		}
		seen[s] = struct{}{}
		out = append(out, s)
	}

	return out
}

// splitCSVOrSpace splits a string by commas or spaces
func splitCSVOrSpace(s string) []string {
	// Replace commas with spaces, then split on spaces
	s = strings.ReplaceAll(s, ",", " ")
	fields := strings.Fields(s)
	return fields
}

// truthy reports if s is a common truthy string
func truthy(s string) bool {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "1", "true", "yes", "on":
		return true
	default:
		return false
	}
}
