package rest

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

const (
	chain138WETHAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
)

type blockscoutCoinStats struct {
	CoinPrice *string `json:"coin_price"`
	CoinImage *string `json:"coin_image"`
}

type tokenAggregationTokenResponse struct {
	Token struct {
		Market struct {
			PriceUsd *float64 `json:"priceUsd"`
		} `json:"market"`
	} `json:"token"`
}

func enrichNativeCoinMarket(ctx context.Context, stats *explorerStats) {
	if stats.CoinPrice != nil && strings.TrimSpace(*stats.CoinPrice) != "" {
		return
	}

	// Prefer on-chain token-aggregation WETH/ETH oracle over Blockscout CoinGecko on Chain 138.
	if price := fetchTokenAggregationEthUsd(ctx); price != "" {
		stats.CoinPrice = &price
		if image := fetchBlockscoutCoinImage(ctx); image != "" {
			stats.CoinImage = &image
		}
		return
	}

	bsURL := strings.TrimSpace(os.Getenv("BLOCKSCOUT_STATS_URL"))
	if bsURL == "" {
		bsURL = "http://127.0.0.1:4000/api/v2/stats"
	}
	if price, image := fetchBlockscoutCoinMarket(ctx, bsURL); price != "" {
		stats.CoinPrice = &price
		if image != "" {
			stats.CoinImage = &image
		}
	}
}

func fetchBlockscoutCoinImage(ctx context.Context) string {
	bsURL := strings.TrimSpace(os.Getenv("BLOCKSCOUT_STATS_URL"))
	if bsURL == "" {
		bsURL = "http://127.0.0.1:4000/api/v2/stats"
	}
	_, image := fetchBlockscoutCoinMarket(ctx, bsURL)
	return image
}

func fetchBlockscoutCoinMarket(ctx context.Context, url string) (price string, image string) {
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return "", ""
	}

	client := &http.Client{Timeout: 4 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", ""
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return "", ""
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
	if err != nil {
		return "", ""
	}

	var payload blockscoutCoinStats
	if err := json.Unmarshal(body, &payload); err != nil {
		return "", ""
	}
	if payload.CoinPrice != nil {
		price = strings.TrimSpace(*payload.CoinPrice)
	}
	if payload.CoinImage != nil {
		image = strings.TrimSpace(*payload.CoinImage)
	}
	return price, image
}

func tokenAggregationAPIV1Base() string {
	base := strings.TrimRight(strings.TrimSpace(firstNonEmptyEnv(
		"TOKEN_AGGREGATION_API_BASE",
		"TOKEN_AGGREGATION_URL",
		"TOKEN_AGGREGATION_BASE_URL",
	)), "/")
	if base == "" {
		return "http://127.0.0.1:3001/api/v1"
	}
	if strings.HasSuffix(base, "/api/v1") {
		return base
	}
	return base + "/api/v1"
}

func fetchTokenAggregationEthUsd(ctx context.Context) string {
	url := fmt.Sprintf("%s/tokens/%s?chainId=138", tokenAggregationAPIV1Base(), chain138WETHAddress)

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return ""
	}

	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return ""
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return ""
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
	if err != nil {
		return ""
	}

	var payload tokenAggregationTokenResponse
	if err := json.Unmarshal(body, &payload); err != nil {
		return ""
	}
	if payload.Token.Market.PriceUsd == nil || *payload.Token.Market.PriceUsd <= 0 {
		return ""
	}
	return formatUsdPrice(*payload.Token.Market.PriceUsd)
}

func formatUsdPrice(value float64) string {
	return fmt.Sprintf("%.2f", value)
}
