package bridge

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

const (
	stargateAPIBase = "https://stargate.finance/api/v1"
	stargateTimeout = 10 * time.Second
)

// chainIDToStargateKey maps chain ID to Stargate chain key
var stargateChainKeys = map[int]string{
	1:      "ethereum",
	10:     "optimism",
	137:    "polygon",
	42161:  "arbitrum",
	8453:   "base",
	56:     "bnb",
	43114:  "avalanche",
	25:     "cronos",
	100:    "gnosis",
	324:    "zksync",
	59144:  "linea",
	534352: "scroll",
}

// Stargate-supported chain IDs
var stargateSupportedChains = map[int]bool{
	1:      true,
	10:     true,
	137:    true,
	42161:  true,
	8453:   true,
	56:     true,
	43114:  true,
	25:     true,
	100:    true,
	324:    true,
	59144:  true,
	534352: true,
}

type stargateQuoteResponse struct {
	Quotes []struct {
		Bridge       string `json:"bridge"`
		SrcAmount    string `json:"srcAmount"`
		DstAmount    string `json:"dstAmount"`
		DstAmountMin string `json:"dstAmountMin"`
		Error        string `json:"error"`
		Duration     *struct {
			Estimated int `json:"estimated"`
		} `json:"duration"`
	} `json:"quotes"`
}

// StargateProvider implements Provider for Stargate (LayerZero)
type StargateProvider struct {
	apiBase string
	client  *http.Client
}

// NewStargateProvider creates a new Stargate bridge provider
func NewStargateProvider() *StargateProvider {
	return &StargateProvider{
		apiBase: stargateAPIBase,
		client: &http.Client{
			Timeout: stargateTimeout,
		},
	}
}

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

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

// GetQuote fetches a bridge quote from the Stargate API
func (p *StargateProvider) GetQuote(ctx context.Context, req *BridgeRequest) (*BridgeQuote, error) {
	srcKey, ok := stargateChainKeys[req.FromChain]
	if !ok {
		return nil, fmt.Errorf("Stargate: unsupported fromChain %d", req.FromChain)
	}
	dstKey, ok := stargateChainKeys[req.ToChain]
	if !ok {
		return nil, fmt.Errorf("Stargate: unsupported toChain %d", req.ToChain)
	}

	if req.Recipient == "" {
		req.Recipient = "0x0000000000000000000000000000000000000000"
	}

	params := url.Values{}
	params.Set("srcToken", req.FromToken)
	params.Set("dstToken", req.ToToken)
	params.Set("srcChainKey", srcKey)
	params.Set("dstChainKey", dstKey)
	params.Set("srcAddress", req.Recipient)
	params.Set("dstAddress", req.Recipient)
	params.Set("srcAmount", req.Amount)
	params.Set("dstAmountMin", "0")

	apiURL := fmt.Sprintf("%s/quotes?%s", p.apiBase, params.Encode())

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
	if err != nil {
		return nil, err
	}

	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("Stargate API error %d: %s", resp.StatusCode, string(body))
	}

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

	var bestIdx = -1
	for i := range stargateResp.Quotes {
		q := &stargateResp.Quotes[i]
		if q.Error != "" {
			continue
		}
		if bestIdx < 0 || q.DstAmount > stargateResp.Quotes[bestIdx].DstAmount {
			bestIdx = i
		}
	}

	if bestIdx < 0 {
		return nil, fmt.Errorf("Stargate: no valid quotes")
	}
	bestQuote := &stargateResp.Quotes[bestIdx]

	estTime := "1-5 min"
	if bestQuote.Duration != nil && bestQuote.Duration.Estimated > 0 {
		estTime = fmt.Sprintf("%d sec", bestQuote.Duration.Estimated)
	}

	return &BridgeQuote{
		Provider:      "Stargate",
		FromChain:     req.FromChain,
		ToChain:       req.ToChain,
		FromAmount:    req.Amount,
		ToAmount:      bestQuote.DstAmount,
		Fee:           "0",
		EstimatedTime: estTime,
		Route: []BridgeStep{{
			Provider: bestQuote.Bridge,
			From:     strconv.Itoa(req.FromChain),
			To:       strconv.Itoa(req.ToChain),
			Type:     "bridge",
		}},
	}, nil
}
