package track1

import (
	"context"
	_ "embed"
	"encoding/json"
	"math/big"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

//go:embed bridge_lanes_default.json
var defaultBridgeLanesJSON []byte

type bridgeLaneDefinition struct {
	Key         string   `json:"key"`
	ChainName   string   `json:"chain_name"`
	ChainID     int64    `json:"chain_id"`
	ConfigReady bool     `json:"config_ready"`
	RPCEnvs     []string `json:"rpc_envs"`
	RPCDefault  string   `json:"rpc_default"`
	LinkToken   string   `json:"link_token"`
	WETH9Bridge string   `json:"weth9_bridge"`
	WETH10Bridge string  `json:"weth10_bridge"`
}

type bridgeLanesConfig struct {
	Updated     string                 `json:"updated"`
	MinLinkWei  string                 `json:"min_link_wei"`
	Lanes       []bridgeLaneDefinition `json:"lanes"`
}

func loadBridgeLanesConfig() bridgeLanesConfig {
	path := strings.TrimSpace(os.Getenv("MISSION_CONTROL_BRIDGE_LANES_JSON"))
	if path != "" {
		if b, err := os.ReadFile(path); err == nil && len(b) > 0 {
			var cfg bridgeLanesConfig
			if json.Unmarshal(b, &cfg) == nil && len(cfg.Lanes) > 0 {
				return cfg
			}
		}
	}
	var cfg bridgeLanesConfig
	_ = json.Unmarshal(defaultBridgeLanesJSON, &cfg)
	return cfg
}

func resolveLaneRPC(def bridgeLaneDefinition) string {
	for _, key := range def.RPCEnvs {
		if value := strings.TrimSpace(os.Getenv(key)); value != "" {
			return value
		}
	}
	for _, row := range ParseExtraRPCProbes() {
		chainKey := row[2]
		if chainKey == strconv.FormatInt(def.ChainID, 10) {
			return row[1]
		}
	}
	return strings.TrimSpace(def.RPCDefault)
}

func erc20BalanceOf(ctx context.Context, rpcURL, tokenAddress, holderAddress string) (string, error) {
	tokenAddress = strings.ToLower(strings.TrimSpace(tokenAddress))
	holderAddress = strings.ToLower(strings.TrimPrefix(strings.TrimSpace(holderAddress), "0x"))
	if len(holderAddress) != 40 {
		return "0", nil
	}
	data := "0x70a08231" + strings.Repeat("0", 24) + holderAddress
	raw, _, err := postJSONRPC(ctx, bridgeLaneHTTPClient(), rpcURL, "eth_call", []interface{}{
		map[string]interface{}{
			"to":   tokenAddress,
			"data": data,
		},
		"latest",
	})
	if err != nil {
		return "", err
	}
	var hex string
	if err := json.Unmarshal(raw, &hex); err != nil {
		return "", err
	}
	hex = strings.TrimSpace(hex)
	if hex == "" || hex == "0x" {
		return "0", nil
	}
	value := new(big.Int)
	if _, ok := value.SetString(strings.TrimPrefix(hex, "0x"), 16); !ok {
		return "0", nil
	}
	return value.String(), nil
}

func bridgeLaneHTTPClient() *http.Client {
	return &http.Client{Timeout: 6 * time.Second}
}

func bridgeFundingStatus(linkBalanceWei, minLinkWei string) string {
	balance, okBalance := new(big.Int).SetString(strings.TrimSpace(linkBalanceWei), 10)
	minimum, okMin := new(big.Int).SetString(strings.TrimSpace(minLinkWei), 10)
	if !okBalance || !okMin {
		return "unknown"
	}
	if balance.Cmp(minimum) >= 0 {
		return "funded"
	}
	if balance.Sign() > 0 {
		return "degraded"
	}
	return "unfunded"
}

func proofStatusForLane(key string, proofs map[string]interface{}) string {
	if proofs == nil {
		return "proof-pending"
	}
	laneProofs, ok := proofs[key].([]interface{})
	if !ok || len(laneProofs) == 0 {
		return "proof-pending"
	}
	for _, item := range laneProofs {
		row, ok := item.(map[string]interface{})
		if !ok {
			continue
		}
		if tx, ok := row["tx_hash"].(string); ok && strings.TrimSpace(tx) != "" {
			return "proof-recorded"
		}
	}
	return "proof-pending"
}

func readProofTransfersJSON() map[string]interface{} {
	path := strings.TrimSpace(os.Getenv("MISSION_CONTROL_PROOF_TRANSFERS_JSON"))
	if path == "" {
		return nil
	}
	b, err := os.ReadFile(path)
	if err != nil || len(b) == 0 {
		return map[string]interface{}{"error": "unreadable or empty", "path": path}
	}
	if len(b) > 512*1024 {
		return map[string]interface{}{"error": "file too large", "path": path}
	}
	var payload map[string]interface{}
	if err := json.Unmarshal(b, &payload); err != nil {
		return map[string]interface{}{"error": err.Error(), "path": path}
	}
	return payload
}

func probeBridgeContract(ctx context.Context, rpcURL, linkToken, bridgeAddress, minLinkWei string) map[string]interface{} {
	result := map[string]interface{}{
		"bridge": strings.TrimSpace(bridgeAddress),
	}
	if rpcURL == "" {
		result["status"] = "unknown"
		result["error"] = "rpc unavailable"
		return result
	}
	if linkToken == "" || bridgeAddress == "" {
		result["status"] = "unknown"
		result["error"] = "missing link token or bridge address"
		return result
	}
	balance, err := erc20BalanceOf(ctx, rpcURL, linkToken, bridgeAddress)
	if err != nil {
		result["status"] = "unknown"
		result["error"] = err.Error()
		return result
	}
	result["link_balance_wei"] = balance
	result["status"] = bridgeFundingStatus(balance, minLinkWei)
	return result
}

func aggregateLaneStatus(weth9Status, weth10Status, proofStatus string) string {
	statuses := []string{weth9Status, weth10Status}
	hasUnfunded := false
	hasDegraded := false
	hasUnknown := false
	for _, status := range statuses {
		switch status {
		case "unfunded":
			hasUnfunded = true
		case "degraded":
			hasDegraded = true
		case "unknown":
			hasUnknown = true
		}
	}
	if hasUnfunded {
		return "unfunded"
	}
	if hasDegraded {
		return "degraded"
	}
	if hasUnknown {
		return "unknown"
	}
	if proofStatus == "proof-pending" {
		return "proof-pending"
	}
	return "funded"
}

func BuildBridgeLaneHealth(ctx context.Context) (map[string]interface{}, map[string]interface{}) {
	cfg := loadBridgeLanesConfig()
	minLinkWei := strings.TrimSpace(cfg.MinLinkWei)
	if minLinkWei == "" {
		minLinkWei = "1000000000000000000"
	}

	proofPayload := readProofTransfersJSON()
	proofByLane := map[string]interface{}{}
	if proofPayload != nil {
		if lanes, ok := proofPayload["lanes"].(map[string]interface{}); ok {
			proofByLane = lanes
		}
	}

	lanes := make([]map[string]interface{}, 0, len(cfg.Lanes))
	for _, def := range cfg.Lanes {
		rpcURL := resolveLaneRPC(def)
		weth9 := probeBridgeContract(ctx, rpcURL, def.LinkToken, def.WETH9Bridge, minLinkWei)
		weth10 := probeBridgeContract(ctx, rpcURL, def.LinkToken, def.WETH10Bridge, minLinkWei)
		weth9Status, _ := weth9["status"].(string)
		weth10Status, _ := weth10["status"].(string)
		proofStatus := proofStatusForLane(def.Key, proofByLane)

		lanes = append(lanes, map[string]interface{}{
			"key":          def.Key,
			"chain_name":   def.ChainName,
			"chain_id":     def.ChainID,
			"config_ready": def.ConfigReady,
			"link_token":   def.LinkToken,
			"status":       aggregateLaneStatus(weth9Status, weth10Status, proofStatus),
			"proof_status": proofStatus,
			"weth9":        weth9,
			"weth10":       weth10,
			"rpc_endpoint": redactRPCOrigin(rpcURL),
		})
	}

	laneHealth := map[string]interface{}{
		"updated_at":   time.Now().UTC().Format(time.RFC3339),
		"min_link_wei": minLinkWei,
		"lanes":        lanes,
	}
	return laneHealth, proofPayload
}
