package rest

import (
	"crypto/sha256"
	_ "embed"
	"encoding/hex"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
)

//go:embed config/metamask/DUAL_CHAIN_NETWORKS.json
var dualChainNetworksJSON []byte

//go:embed config/metamask/DUAL_CHAIN_TOKEN_LIST.tokenlist.json
var dualChainTokenListJSON []byte

//go:embed config/metamask/CHAIN138_RPC_CAPABILITIES.json
var chain138RPCCapabilitiesJSON []byte

type configPayload struct {
	body    []byte
	source  string
	modTime time.Time
}

func uniqueConfigPaths(paths []string) []string {
	seen := make(map[string]struct{}, len(paths))
	out := make([]string, 0, len(paths))
	for _, candidate := range paths {
		trimmed := strings.TrimSpace(candidate)
		if trimmed == "" {
			continue
		}
		if _, ok := seen[trimmed]; ok {
			continue
		}
		seen[trimmed] = struct{}{}
		out = append(out, trimmed)
	}
	return out
}

func buildConfigCandidates(envKeys []string, defaults []string) []string {
	candidates := make([]string, 0, len(envKeys)+len(defaults)*4)
	for _, key := range envKeys {
		if value := strings.TrimSpace(os.Getenv(key)); value != "" {
			candidates = append(candidates, value)
		}
	}

	if cwd, err := os.Getwd(); err == nil {
		for _, rel := range defaults {
			if filepath.IsAbs(rel) {
				candidates = append(candidates, rel)
				continue
			}
			candidates = append(candidates, filepath.Join(cwd, rel))
			candidates = append(candidates, rel)
		}
	}

	if exe, err := os.Executable(); err == nil {
		exeDir := filepath.Dir(exe)
		for _, rel := range defaults {
			if filepath.IsAbs(rel) {
				continue
			}
			candidates = append(candidates,
				filepath.Join(exeDir, rel),
				filepath.Join(exeDir, "..", rel),
				filepath.Join(exeDir, "..", "..", rel),
			)
		}
	}

	return uniqueConfigPaths(candidates)
}

func loadConfigPayload(envKeys []string, defaults []string, embedded []byte) configPayload {
	for _, candidate := range buildConfigCandidates(envKeys, defaults) {
		body, err := os.ReadFile(candidate)
		if err != nil || len(body) == 0 {
			continue
		}
		payload := configPayload{
			body:   body,
			source: "runtime-file",
		}
		if info, statErr := os.Stat(candidate); statErr == nil {
			payload.modTime = info.ModTime().UTC()
		}
		return payload
	}

	return configPayload{
		body:   embedded,
		source: "embedded",
	}
}

func payloadETag(body []byte) string {
	sum := sha256.Sum256(body)
	return `W/"` + hex.EncodeToString(sum[:]) + `"`
}

func serveJSONConfig(w http.ResponseWriter, r *http.Request, payload configPayload, cacheControl string) {
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Cache-Control", cacheControl)
	w.Header().Set("X-Config-Source", payload.source)

	etag := payloadETag(payload.body)
	w.Header().Set("ETag", etag)
	if !payload.modTime.IsZero() {
		w.Header().Set("Last-Modified", payload.modTime.Format(http.TimeFormat))
	}

	if match := strings.TrimSpace(r.Header.Get("If-None-Match")); match != "" && strings.Contains(match, etag) {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	_, _ = w.Write(payload.body)
}

// handleConfigNetworks serves GET /api/config/networks (Chain 138 + Ethereum Mainnet params for wallet_addEthereumChain).
func (s *Server) handleConfigNetworks(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		w.Header().Set("Allow", "GET")
		writeMethodNotAllowed(w)
		return
	}
	payload := loadConfigPayload(
		[]string{"CONFIG_NETWORKS_JSON_PATH", "NETWORKS_CONFIG_JSON_PATH"},
		[]string{
			"explorer-monorepo/backend/api/rest/config/metamask/DUAL_CHAIN_NETWORKS.json",
			"backend/api/rest/config/metamask/DUAL_CHAIN_NETWORKS.json",
			"api/rest/config/metamask/DUAL_CHAIN_NETWORKS.json",
			"config/metamask/DUAL_CHAIN_NETWORKS.json",
		},
		dualChainNetworksJSON,
	)
	serveJSONConfig(w, r, payload, "public, max-age=0, must-revalidate")
}

// handleConfigTokenList serves GET /api/config/token-list (Uniswap token list format for MetaMask).
func (s *Server) handleConfigTokenList(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		w.Header().Set("Allow", "GET")
		writeMethodNotAllowed(w)
		return
	}
	payload := loadConfigPayload(
		[]string{"CONFIG_TOKEN_LIST_JSON_PATH", "TOKEN_LIST_CONFIG_JSON_PATH"},
		[]string{
			"explorer-monorepo/backend/api/rest/config/metamask/DUAL_CHAIN_TOKEN_LIST.tokenlist.json",
			"backend/api/rest/config/metamask/DUAL_CHAIN_TOKEN_LIST.tokenlist.json",
			"api/rest/config/metamask/DUAL_CHAIN_TOKEN_LIST.tokenlist.json",
			"config/metamask/DUAL_CHAIN_TOKEN_LIST.tokenlist.json",
		},
		dualChainTokenListJSON,
	)
	serveJSONConfig(w, r, payload, "public, max-age=0, must-revalidate")
}

// handleConfigCapabilities serves GET /api/config/capabilities (Chain 138 wallet/RPC capability matrix).
func (s *Server) handleConfigCapabilities(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		w.Header().Set("Allow", "GET")
		writeMethodNotAllowed(w)
		return
	}
	payload := loadConfigPayload(
		[]string{"CONFIG_CAPABILITIES_JSON_PATH", "RPC_CAPABILITIES_JSON_PATH"},
		[]string{
			"explorer-monorepo/backend/api/rest/config/metamask/CHAIN138_RPC_CAPABILITIES.json",
			"backend/api/rest/config/metamask/CHAIN138_RPC_CAPABILITIES.json",
			"api/rest/config/metamask/CHAIN138_RPC_CAPABILITIES.json",
			"config/metamask/CHAIN138_RPC_CAPABILITIES.json",
		},
		chain138RPCCapabilitiesJSON,
	)
	serveJSONConfig(w, r, payload, "public, max-age=0, must-revalidate")
}
