package bridge

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

const (
	relayAPIBase = "https://api.relay.link"
	relayTimeout = 10 * time.Second
)

// Relay-supported chain IDs (EVM chains, configurable)
var relaySupportedChains = map[int]bool{
	1:      true, // Ethereum
	10:     true, // Optimism
	137:    true, // Polygon
	42161:  true, // Arbitrum
	8453:   true, // Base
	56:     true, // BNB Chain
	43114:  true, // Avalanche
	100:    true, // Gnosis
	25:     true, // Cronos
	324:    true, // zkSync
	59144:  true, // Linea
	534352: true, // Scroll
}

type relayQuoteRequest struct {
	User                string `json:"user"`
	OriginChainID       int    `json:"originChainId"`
	DestinationChainID  int    `json:"destinationChainId"`
	OriginCurrency      string `json:"originCurrency"`
	DestinationCurrency string `json:"destinationCurrency"`
	Amount              string `json:"amount"`
	TradeType           string `json:"tradeType"`
	Recipient           string `json:"recipient,omitempty"`
}

type relayQuoteResponse struct {
	Details *struct {
		CurrencyOut *struct {
			Amount string `json:"amount"`
		} `json:"currencyOut"`
	} `json:"details"`
}

// RelayProvider implements Provider for Relay.link
type RelayProvider struct {
	apiBase string
	client  *http.Client
}

// NewRelayProvider creates a new Relay.link bridge provider
func NewRelayProvider() *RelayProvider {
	return &RelayProvider{
		apiBase: relayAPIBase,
		client: &http.Client{
			Timeout: relayTimeout,
		},
	}
}

// Name returns the provider name
func (p *RelayProvider) Name() string {
	return "Relay"
}

// SupportsRoute returns true if Relay supports the fromChain->toChain route
func (p *RelayProvider) SupportsRoute(fromChain, toChain int) bool {
	return relaySupportedChains[fromChain] && relaySupportedChains[toChain]
}

// GetQuote fetches a bridge quote from the Relay API
func (p *RelayProvider) GetQuote(ctx context.Context, req *BridgeRequest) (*BridgeQuote, error) {
	if req.Recipient == "" {
		return nil, fmt.Errorf("Relay: recipient address required")
	}

	bodyReq := relayQuoteRequest{
		User:                req.Recipient,
		OriginChainID:       req.FromChain,
		DestinationChainID:  req.ToChain,
		OriginCurrency:      req.FromToken,
		DestinationCurrency: req.ToToken,
		Amount:              req.Amount,
		TradeType:           "EXACT_INPUT",
		Recipient:           req.Recipient,
	}
	jsonBody, err := json.Marshal(bodyReq)
	if err != nil {
		return nil, err
	}

	apiURL := p.apiBase + "/quote/v2"
	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(jsonBody))
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := p.client.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Relay API error %d: %s", resp.StatusCode, string(body))
	}

	var relayResp relayQuoteResponse
	if err := json.Unmarshal(body, &relayResp); err != nil {
		return nil, fmt.Errorf("failed to parse Relay response: %w", err)
	}

	toAmount := ""
	if relayResp.Details != nil && relayResp.Details.CurrencyOut != nil {
		toAmount = relayResp.Details.CurrencyOut.Amount
	}
	if toAmount == "" {
		return nil, fmt.Errorf("Relay: no quote amount")
	}

	steps := []BridgeStep{{Provider: "Relay", From: strconv.Itoa(req.FromChain), To: strconv.Itoa(req.ToChain), Type: "bridge"}}

	return &BridgeQuote{
		Provider:      "Relay",
		FromChain:     req.FromChain,
		ToChain:       req.ToChain,
		FromAmount:    req.Amount,
		ToAmount:      toAmount,
		Fee:           "0",
		EstimatedTime: "1-5 min",
		Route:         steps,
	}, nil
}
