package bridge

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

const (
	socketAPIBase = "https://public-backend.bungee.exchange"
	socketTimeout = 10 * time.Second
)

var socketSupportedChains = map[int]bool{
	1: true, 10: true, 137: true, 42161: true, 8453: true,
	56: true, 43114: true, 100: true, 25: true, 250: true,
	324: true, 59144: true, 534352: true, 42220: true, 5000: true, 1111: true,
}

type socketQuoteResponse struct {
	Success bool `json:"success"`
	Result  *struct {
		Route *struct {
			ToAmount    string `json:"toAmount"`
			ToAmountMin string `json:"toAmountMin"`
		} `json:"route"`
	} `json:"result"`
	Message string `json:"message"`
}

type SocketProvider struct {
	apiBase string
	client  *http.Client
}

func NewSocketProvider() *SocketProvider {
	return &SocketProvider{apiBase: socketAPIBase, client: &http.Client{Timeout: socketTimeout}}
}

func (p *SocketProvider) Name() string { return "Socket" }

func (p *SocketProvider) SupportsRoute(fromChain, toChain int) bool {
	return socketSupportedChains[fromChain] && socketSupportedChains[toChain]
}

func (p *SocketProvider) GetQuote(ctx context.Context, req *BridgeRequest) (*BridgeQuote, error) {
	if req.Recipient == "" {
		return nil, fmt.Errorf("Socket: recipient required")
	}
	params := url.Values{}
	params.Set("fromChainId", strconv.Itoa(req.FromChain))
	params.Set("toChainId", strconv.Itoa(req.ToChain))
	params.Set("fromTokenAddress", req.FromToken)
	params.Set("toTokenAddress", req.ToToken)
	params.Set("fromAmount", req.Amount)
	params.Set("recipient", req.Recipient)
	apiURL := fmt.Sprintf("%s/api/v1/bungee/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()
	body, _ := io.ReadAll(resp.Body)
	var r socketQuoteResponse
	if err := json.Unmarshal(body, &r); err != nil {
		return nil, fmt.Errorf("Socket parse error: %w", err)
	}
	if !r.Success || r.Result == nil || r.Result.Route == nil {
		return nil, fmt.Errorf("Socket API: %s", r.Message)
	}
	toAmount := r.Result.Route.ToAmount
	if toAmount == "" {
		toAmount = r.Result.Route.ToAmountMin
	}
	if toAmount == "" {
		return nil, fmt.Errorf("Socket: no amount")
	}
	steps := []BridgeStep{{Provider: "Socket", From: strconv.Itoa(req.FromChain), To: strconv.Itoa(req.ToChain), Type: "bridge"}}
	return &BridgeQuote{
		Provider: "Socket", FromChain: req.FromChain, ToChain: req.ToChain,
		FromAmount: req.Amount, ToAmount: toAmount, Fee: "0", EstimatedTime: "1-5 min", Route: steps,
	}, nil
}
