package track1

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strconv"
	"testing"
	"time"

	"github.com/explorer/backend/api/freshness"
	"github.com/stretchr/testify/require"
)

func TestFetchCCIPRelayHealthFromURL(t *testing.T) {
	relay := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"ok":true,"status":"operational","destination":{"chain_name":"Ethereum Mainnet"},"queue":{"size":0}}`))
	}))
	defer relay.Close()

	t.Setenv("CCIP_RELAY_HEALTH_URL", relay.URL+"/healthz")
	t.Setenv("CCIP_RELAY_HEALTH_URLS", "")
	t.Setenv("MISSION_CONTROL_CCIP_JSON", "")

	got := FetchCCIPRelayHealth(context.Background())
	require.NotNil(t, got)

	probe, ok := got["url_probe"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, true, probe["ok"])

	body, ok := probe["body"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, "operational", body["status"])

	dest, ok := body["destination"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, "Ethereum Mainnet", dest["chain_name"])
}

func TestFetchCCIPRelayHealthsFromNamedURLs(t *testing.T) {
	mainnet := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"status":"operational","destination":{"chain_name":"Ethereum Mainnet"},"queue":{"size":0}}`))
	}))
	defer mainnet.Close()

	bsc := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"status":"operational","destination":{"chain_name":"BSC"},"queue":{"size":1}}`))
	}))
	defer bsc.Close()

	t.Setenv("CCIP_RELAY_HEALTH_URL", "")
	t.Setenv("MISSION_CONTROL_CCIP_JSON", "")
	t.Setenv("CCIP_RELAY_HEALTH_URLS", "mainnet="+mainnet.URL+"/healthz,bsc="+bsc.URL+"/healthz")

	got := FetchCCIPRelayHealths(context.Background())
	require.NotNil(t, got)

	mainnetRelay, ok := got["mainnet"].(map[string]interface{})
	require.True(t, ok)
	mainnetProbe, ok := mainnetRelay["url_probe"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, true, mainnetProbe["ok"])

	bscRelay, ok := got["bsc"].(map[string]interface{})
	require.True(t, ok)
	bscProbe, ok := bscRelay["url_probe"].(map[string]interface{})
	require.True(t, ok)
	body, ok := bscProbe["body"].(map[string]interface{})
	require.True(t, ok)
	dest, ok := body["destination"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, "BSC", dest["chain_name"])
}

func TestFetchCCIPRelayHealthPrefersMainnetCW(t *testing.T) {
	relays := map[string]interface{}{
		"mainnet_weth": map[string]interface{}{"url_probe": map[string]interface{}{"ok": true}},
		"mainnet_cw":   map[string]interface{}{"url_probe": map[string]interface{}{"ok": true, "body": map[string]interface{}{"status": "operational"}}},
		"bsc":          map[string]interface{}{"url_probe": map[string]interface{}{"ok": true}},
	}

	got := primaryRelayHealth(relays)
	require.NotNil(t, got)
	require.Equal(t, relays["mainnet_cw"], got)
}

func TestFetchCCIPRelayHealthFromFileSnapshot(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "relay-health.json")
	require.NoError(t, os.WriteFile(path, []byte(`{"status":"paused","queue":{"size":3}}`), 0o644))

	t.Setenv("CCIP_RELAY_HEALTH_URL", "")
	t.Setenv("CCIP_RELAY_HEALTH_URLS", "")
	t.Setenv("MISSION_CONTROL_CCIP_JSON", path)

	got := FetchCCIPRelayHealth(context.Background())
	require.NotNil(t, got)

	snapshot, ok := got["file_snapshot"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, "paused", snapshot["status"])

	queue, ok := snapshot["queue"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, float64(3), queue["size"])
}

func TestBuildBridgeStatusDataIncludesCCIPRelay(t *testing.T) {
	rpc := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var req struct {
			Method string `json:"method"`
		}
		require.NoError(t, json.NewDecoder(r.Body).Decode(&req))

		w.Header().Set("Content-Type", "application/json")
		switch req.Method {
		case "eth_blockNumber":
			_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":"0x10"}`))
		case "eth_getBlockByNumber":
			ts := strconv.FormatInt(time.Now().Add(-2*time.Second).Unix(), 16)
			_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":{"timestamp":"0x` + ts + `"}}`))
		default:
			http.Error(w, `{"jsonrpc":"2.0","id":1,"error":{"message":"unsupported"}}`, http.StatusBadRequest)
		}
	}))
	defer rpc.Close()

	relay := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"ok":true,"status":"operational","queue":{"size":0}}`))
	}))
	defer relay.Close()

	t.Setenv("RPC_URL", rpc.URL)
	t.Setenv("ETH_MAINNET_RPC_URL", "")
	t.Setenv("MISSION_CONTROL_EXTRA_RPCS", "")
	t.Setenv("MISSION_CONTROL_VERIFY_JSON", "")
	t.Setenv("CCIP_RELAY_HEALTH_URL", relay.URL+"/healthz")
	t.Setenv("CCIP_RELAY_HEALTH_URLS", "")
	t.Setenv("MISSION_CONTROL_CCIP_JSON", "")

	s := &Server{
		freshnessLoader: func(context.Context) (*freshness.Snapshot, *freshness.SummaryCompleteness, *freshness.Sampling, *freshness.Diagnostics, error) {
			now := time.Now().UTC().Format(time.RFC3339)
			head := int64(16)
			txBlock := int64(12)
			distance := int64(4)
			return &freshness.Snapshot{
					ChainHead: freshness.Reference{
						BlockNumber:  &head,
						Timestamp:    &now,
						AgeSeconds:   func() *int64 { v := int64(1); return &v }(),
						Source:       freshness.SourceReported,
						Confidence:   freshness.ConfidenceHigh,
						Provenance:   freshness.ProvenanceRPC,
						Completeness: freshness.CompletenessComplete,
					},
					LatestIndexedTransaction: freshness.Reference{
						BlockNumber:  &txBlock,
						Timestamp:    &now,
						AgeSeconds:   func() *int64 { v := int64(120); return &v }(),
						Source:       freshness.SourceReported,
						Confidence:   freshness.ConfidenceHigh,
						Provenance:   freshness.ProvenanceTxIndex,
						Completeness: freshness.CompletenessPartial,
					},
					LatestNonEmptyBlock: freshness.Reference{
						BlockNumber:      &txBlock,
						Timestamp:        &now,
						AgeSeconds:       func() *int64 { v := int64(120); return &v }(),
						DistanceFromHead: &distance,
						Source:           freshness.SourceReported,
						Confidence:       freshness.ConfidenceHigh,
						Provenance:       freshness.ProvenanceTxIndex,
						Completeness:     freshness.CompletenessPartial,
					},
				},
				&freshness.SummaryCompleteness{
					TransactionsFeed: freshness.CompletenessPartial,
					BlocksFeed:       freshness.CompletenessComplete,
				},
				&freshness.Sampling{StatsGeneratedAt: &now},
				&freshness.Diagnostics{
					TxVisibilityState: "lagging",
					ActivityState:     "fresh_head_stale_transaction_visibility",
					Source:            freshness.SourceReported,
					Confidence:        freshness.ConfidenceMedium,
					Provenance:        freshness.ProvenanceComposite,
					Completeness:      freshness.CompletenessPartial,
				},
				nil
		},
	}
	got := s.BuildBridgeStatusData(context.Background())
	ccip, ok := got["ccip_relay"].(map[string]interface{})
	require.True(t, ok)
	relays, ok := got["ccip_relays"].(map[string]interface{})
	require.True(t, ok)
	require.Contains(t, relays, "mainnet")

	probe, ok := ccip["url_probe"].(map[string]interface{})
	require.True(t, ok)
	require.Equal(t, true, probe["ok"])
	require.Contains(t, got, "freshness")
	require.Contains(t, got, "diagnostics")
	require.Contains(t, got, "subsystems")
	require.Contains(t, got, "mode")
}

func TestBuildBridgeStatusDataDegradesWhenNamedRelayFails(t *testing.T) {
	rpc := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var req struct {
			Method string `json:"method"`
		}
		require.NoError(t, json.NewDecoder(r.Body).Decode(&req))

		w.Header().Set("Content-Type", "application/json")
		switch req.Method {
		case "eth_blockNumber":
			_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":"0x10"}`))
		case "eth_getBlockByNumber":
			ts := strconv.FormatInt(time.Now().Add(-2*time.Second).Unix(), 16)
			_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":{"timestamp":"0x` + ts + `"}}`))
		default:
			http.Error(w, `{"jsonrpc":"2.0","id":1,"error":{"message":"unsupported"}}`, http.StatusBadRequest)
		}
	}))
	defer rpc.Close()

	mainnet := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"status":"operational","queue":{"size":0}}`))
	}))
	defer mainnet.Close()

	bad := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, `{"status":"degraded"}`, http.StatusBadGateway)
	}))
	defer bad.Close()

	t.Setenv("RPC_URL", rpc.URL)
	t.Setenv("ETH_MAINNET_RPC_URL", "")
	t.Setenv("MISSION_CONTROL_EXTRA_RPCS", "")
	t.Setenv("MISSION_CONTROL_VERIFY_JSON", "")
	t.Setenv("CCIP_RELAY_HEALTH_URL", "")
	t.Setenv("MISSION_CONTROL_CCIP_JSON", "")
	t.Setenv("CCIP_RELAY_HEALTH_URLS", "mainnet="+mainnet.URL+"/healthz,bsc="+bad.URL+"/healthz")

	s := &Server{
		freshnessLoader: func(context.Context) (*freshness.Snapshot, *freshness.SummaryCompleteness, *freshness.Sampling, *freshness.Diagnostics, error) {
			return nil, nil, nil, nil, nil
		},
	}
	got := s.BuildBridgeStatusData(context.Background())
	require.Equal(t, "degraded", got["status"])
}
