package track1

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
)

type relayHealthTarget struct {
	Name string
	URL  string
}

func fetchRelayHealthURL(ctx context.Context, u string) map[string]interface{} {
	out := make(map[string]interface{})

	c := &http.Client{Timeout: 4 * time.Second}
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
	if err != nil {
		out["url_probe"] = map[string]interface{}{"ok": false, "error": err.Error()}
	} else {
		resp, err := c.Do(req)
		if err != nil {
			out["url_probe"] = map[string]interface{}{"ok": false, "error": err.Error()}
		} else {
			func() {
				defer resp.Body.Close()
				b, _ := io.ReadAll(io.LimitReader(resp.Body, 256*1024))
				ok := resp.StatusCode >= 200 && resp.StatusCode < 300
				var j interface{}
				if json.Unmarshal(b, &j) == nil {
					out["url_probe"] = map[string]interface{}{"ok": ok, "status": resp.StatusCode, "body": j}
				} else {
					out["url_probe"] = map[string]interface{}{"ok": ok, "status": resp.StatusCode, "raw": string(b)}
				}
			}()
		}
	}

	return out
}

func fetchRelayHealthFileSnapshot(p string) map[string]interface{} {
	out := make(map[string]interface{})
	if p != "" {
		b, err := os.ReadFile(p)
		if err != nil {
			out["file_snapshot_error"] = err.Error()
		} else if len(b) > 512*1024 {
			out["file_snapshot_error"] = "file too large"
		} else {
			var j interface{}
			if err := json.Unmarshal(b, &j); err != nil {
				out["file_snapshot_error"] = err.Error()
			} else {
				out["file_snapshot"] = j
			}
		}
	}
	return out
}

func buildRelayHealthSignal(ctx context.Context, url, filePath string) map[string]interface{} {
	out := make(map[string]interface{})
	if strings.TrimSpace(url) != "" {
		for key, value := range fetchRelayHealthURL(ctx, url) {
			out[key] = value
		}
	}
	if strings.TrimSpace(filePath) != "" {
		for key, value := range fetchRelayHealthFileSnapshot(filePath) {
			out[key] = value
		}
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

func normalizeRelayHealthName(raw string, index int) string {
	name := strings.TrimSpace(strings.ToLower(raw))
	if name == "" {
		return "relay_" + strconv.Itoa(index)
	}
	replacer := strings.NewReplacer(" ", "_", "-", "_", "/", "_")
	name = replacer.Replace(name)
	return name
}

func parseRelayHealthTargets() []relayHealthTarget {
	raw := strings.TrimSpace(os.Getenv("CCIP_RELAY_HEALTH_URLS"))
	if raw == "" {
		return nil
	}

	normalized := strings.NewReplacer("\n", ",", ";", ",").Replace(raw)
	parts := strings.Split(normalized, ",")
	targets := make([]relayHealthTarget, 0, len(parts))
	for idx, part := range parts {
		part = strings.TrimSpace(part)
		if part == "" {
			continue
		}
		name := ""
		url := part
		if strings.Contains(part, "=") {
			chunks := strings.SplitN(part, "=", 2)
			name = normalizeRelayHealthName(chunks[0], idx+1)
			url = strings.TrimSpace(chunks[1])
		} else {
			name = normalizeRelayHealthName("", idx+1)
		}
		if url == "" {
			continue
		}
		targets = append(targets, relayHealthTarget{Name: name, URL: url})
	}
	return targets
}

// FetchCCIPRelayHealths returns optional named CCIP / relay signals from URL probes and/or operator JSON files.
// Safe defaults: short timeouts, small body cap. Omit from payload when nothing is configured.
func FetchCCIPRelayHealths(ctx context.Context) map[string]interface{} {
	relays := make(map[string]interface{})

	if legacy := buildRelayHealthSignal(
		ctx,
		strings.TrimSpace(os.Getenv("CCIP_RELAY_HEALTH_URL")),
		strings.TrimSpace(os.Getenv("MISSION_CONTROL_CCIP_JSON")),
	); legacy != nil {
		relays["mainnet"] = legacy
	}

	for _, target := range parseRelayHealthTargets() {
		if _, exists := relays[target.Name]; exists {
			continue
		}
		if relay := buildRelayHealthSignal(ctx, target.URL, ""); relay != nil {
			relays[target.Name] = relay
		}
	}

	if len(relays) == 0 {
		return nil
	}
	return relays
}

func primaryRelayHealth(relays map[string]interface{}) map[string]interface{} {
	if len(relays) == 0 {
		return nil
	}
	preferred := []string{"mainnet_cw", "mainnet_weth", "mainnet"}
	for _, key := range preferred {
		if relay, ok := relays[key].(map[string]interface{}); ok {
			return relay
		}
	}
	keys := make([]string, 0, len(relays))
	for key := range relays {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		if relay, ok := relays[key].(map[string]interface{}); ok {
			return relay
		}
	}
	return nil
}

// FetchCCIPRelayHealth returns the primary relay signal for legacy callers.
func FetchCCIPRelayHealth(ctx context.Context) map[string]interface{} {
	return primaryRelayHealth(FetchCCIPRelayHealths(ctx))
}
