package bridge

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

const (
	hopAPIBase = "https://api.hop.exchange"
	hopTimeout = 10 * time.Second
)

// Hop-supported chain IDs: ethereum, optimism, arbitrum, polygon, gnosis, nova, base
var hopSupportedChains = map[int]bool{
	1:     true, // ethereum
	10:    true, // optimism
	42161: true, // arbitrum
	137:   true, // polygon
	100:   true, // gnosis
	42170: true, // nova
	8453:  true, // base
}

var hopChainIdToSlug = map[int]string{
	1:     "ethereum",
	10:    "optimism",
	42161: "arbitrum",
	137:   "polygon",
	100:   "gnosis",
	42170: "nova",
	8453:  "base",
}

// hopQuoteResponse represents Hop API /v1/quote response
type hopQuoteResponse struct {
	AmountIn                string  `json:"amountIn"`
	Slippage                float64 `json:"slippage"`
	AmountOutMin            string  `json:"amountOutMin"`
	DestinationAmountOutMin string  `json:"destinationAmountOutMin"`
	BonderFee               string  `json:"bonderFee"`
	EstimatedReceived       string  `json:"estimatedReceived"`
}

// HopProvider implements Provider for Hop Protocol
type HopProvider struct {
	apiBase string
	client  *http.Client
}

// NewHopProvider creates a new Hop Protocol bridge provider
func NewHopProvider() *HopProvider {
	return &HopProvider{
		apiBase: hopAPIBase,
		client: &http.Client{
			Timeout: hopTimeout,
		},
	}
}

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

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

// GetQuote fetches a bridge quote from the Hop API
func (p *HopProvider) GetQuote(ctx context.Context, req *BridgeRequest) (*BridgeQuote, error) {
	fromSlug, ok := hopChainIdToSlug[req.FromChain]
	if !ok {
		return nil, fmt.Errorf("Hop: unsupported source chain %d", req.FromChain)
	}
	toSlug, ok := hopChainIdToSlug[req.ToChain]
	if !ok {
		return nil, fmt.Errorf("Hop: unsupported destination chain %d", req.ToChain)
	}
	if fromSlug == toSlug {
		return nil, fmt.Errorf("Hop: source and destination must differ")
	}

	// Hop token symbols: USDC, USDT, DAI, ETH, MATIC, xDAI
	params := url.Values{}
	params.Set("amount", req.Amount)
	params.Set("token", mapTokenToHop(req.FromToken))
	params.Set("fromChain", fromSlug)
	params.Set("toChain", toSlug)
	params.Set("slippage", "0.5")

	apiURL := fmt.Sprintf("%s/v1/quote?%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()

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

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

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

	toAmount := hopResp.EstimatedReceived
	if toAmount == "" {
		toAmount = hopResp.AmountIn
	}

	return &BridgeQuote{
		Provider:      "Hop",
		FromChain:     req.FromChain,
		ToChain:       req.ToChain,
		FromAmount:    req.Amount,
		ToAmount:      toAmount,
		Fee:           hopResp.BonderFee,
		EstimatedTime: "2-5 min",
		Route: []BridgeStep{
			{
				Provider: "Hop",
				From:     strconv.Itoa(req.FromChain),
				To:       strconv.Itoa(req.ToChain),
				Type:     "bridge",
			},
		},
	}, nil
}

// mapTokenToHop maps token address/symbol to Hop token symbol
func mapTokenToHop(token string) string {
	// Common mappings - extend as needed
	switch token {
	case "USDC", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48":
		return "USDC"
	case "USDT", "0xdAC17F958D2ee523a2206206994597C13D831ec7":
		return "USDT"
	case "DAI", "0x6B175474E89094C44Da98b954EedeAC495271d0F":
		return "DAI"
	case "ETH", "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "0x0000000000000000000000000000000000000000":
		return "ETH"
	case "MATIC":
		return "MATIC"
	case "xDAI":
		return "xDAI"
	default:
		return "USDC"
	}
}
