package bridge

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

const (
	lifiAPIBase = "https://li.quest"
	lifiTimeout = 10 * time.Second
)

// LiFi-supported chain IDs for SupportsRoute (subset of Li.Fi's 40+ chains)
var lifiSupportedChains = map[int]bool{
	1:      true, // Ethereum Mainnet
	137:    true, // Polygon
	10:     true, // Optimism
	8453:   true, // Base
	42161:  true, // Arbitrum One
	56:     true, // BNB Chain
	43114:  true, // Avalanche
	100:    true, // Gnosis Chain
	42220:  true, // Celo
	324:    true, // zkSync Era
	59144:  true, // Linea
	5000:   true, // Mantle
	534352: true, // Scroll
	25:     true, // Cronos
	250:    true, // Fantom
	1111:   true, // Wemix
}

// lifiQuoteResponse represents the Li.Fi API quote response structure
type lifiQuoteResponse struct {
	ID       string `json:"id"`
	Type     string `json:"type"`
	Tool     string `json:"tool"`
	Estimate *struct {
		FromAmount  string `json:"fromAmount"`
		ToAmount    string `json:"toAmount"`
		ToAmountMin string `json:"toAmountMin"`
	} `json:"estimate"`
	IncludedSteps []struct {
		Type     string `json:"type"`
		Tool     string `json:"tool"`
		Estimate *struct {
			FromAmount string `json:"fromAmount"`
			ToAmount   string `json:"toAmount"`
		} `json:"estimate"`
	} `json:"includedSteps"`
}

// LiFiProvider implements Provider for Li.Fi bridge aggregator
type LiFiProvider struct {
	apiBase    string
	client     *http.Client
	integrator string
}

// NewLiFiProvider creates a new Li.Fi bridge provider. cfg can be nil (uses DefaultConfig).
func NewLiFiProvider(cfg *Config) *LiFiProvider {
	if cfg == nil {
		cfg = DefaultConfig()
	}
	return &LiFiProvider{
		apiBase:    lifiAPIBase,
		client:     &http.Client{Timeout: lifiTimeout},
		integrator: cfg.IntegratorName,
	}
}

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

// SupportsRoute returns true if Li.Fi supports the fromChain->toChain route
func (p *LiFiProvider) SupportsRoute(fromChain, toChain int) bool {
	return lifiSupportedChains[fromChain] && lifiSupportedChains[toChain]
}

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

	params := url.Values{}
	params.Set("fromChain", strconv.Itoa(req.FromChain))
	params.Set("toChain", strconv.Itoa(req.ToChain))
	params.Set("fromToken", req.FromToken)
	params.Set("toToken", req.ToToken)
	params.Set("fromAmount", req.Amount)
	params.Set("fromAddress", req.Recipient)
	params.Set("toAddress", req.Recipient)
	params.Set("integrator", p.integrator)

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

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

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

	if lifiResp.Estimate == nil {
		return nil, fmt.Errorf("Li.Fi response missing estimate")
	}

	toAmount := lifiResp.Estimate.ToAmount
	if toAmount == "" && len(lifiResp.IncludedSteps) > 0 && lifiResp.IncludedSteps[len(lifiResp.IncludedSteps)-1].Estimate != nil {
		toAmount = lifiResp.IncludedSteps[len(lifiResp.IncludedSteps)-1].Estimate.ToAmount
	}
	if toAmount == "" {
		return nil, fmt.Errorf("Li.Fi response missing toAmount")
	}

	route := make([]BridgeStep, 0, len(lifiResp.IncludedSteps))
	for _, step := range lifiResp.IncludedSteps {
		stepType := "bridge"
		if step.Type == "swap" {
			stepType = "swap"
		} else if step.Type == "cross" {
			stepType = "bridge"
		}
		route = append(route, BridgeStep{
			Provider: step.Tool,
			From:     strconv.Itoa(req.FromChain),
			To:       strconv.Itoa(req.ToChain),
			Type:     stepType,
		})
	}
	if len(route) == 0 {
		route = append(route, BridgeStep{
			Provider: lifiResp.Tool,
			From:     strconv.Itoa(req.FromChain),
			To:       strconv.Itoa(req.ToChain),
			Type:     lifiResp.Type,
		})
	}

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