package yahooFinance

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"net/url"
	"time"

	"github.com/pardnchiu/agenvoy/internal/runtime/torii"
	go_pkg_http "github.com/pardnchiu/go-pkg/http"
)

const (
	ttl = 60
)

var (
	paths = []string{"query1.finance.yahoo.com", "query2.finance.yahoo.com"}
)

func handler(ctx context.Context, symbol, timeInterval, timeRange string) (string, error) {
	hash := sha256.Sum256([]byte(symbol + "|" + timeInterval + "|" + timeRange))
	cacheKey := "yahoo:" + hex.EncodeToString(hash[:])
	db := torii.DB(torii.DBToolCache)
	if entry, ok := db.Get(cacheKey); ok {
		return entry.Value(), nil
	}

	type result struct {
		data string
		err  error
	}

	ch := make(chan result, 2)

	for _, host := range paths {
		go func(h string) {
			data, err := fetch(ctx, h, symbol, timeInterval, timeRange)
			if err != nil {
				err = fmt.Errorf("failed to fetch yahoo finance: %w", err)
			}
			ch <- result{data, err}
		}(host)
	}

	winCh := make(chan result, 1)
	go func() {
		var firstErr result
		for range 2 {
			r := <-ch
			if r.err == nil {
				winCh <- r
				return
			}
			if firstErr.err == nil {
				firstErr = r
			}
		}
		winCh <- firstErr
	}()

	select {
	case <-ctx.Done():
		return "", ctx.Err()

	case r := <-winCh:
		if r.err == nil && r.data != "" {
			if err := db.Set(cacheKey, r.data, torii.SetDefault, torii.TTL(ttl)); err != nil {
				slog.Warn("db.Set",
					slog.String("error", err.Error()))
			}
		}
		return r.data, r.err
	}
}

func fetch(ctx context.Context, host, symbol, interval, rangeStr string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	q := url.Values{}
	q.Set("interval", interval)
	q.Set("range", rangeStr)
	path := fmt.Sprintf("https://%s/v8/finance/chart/%s?%s",
		host,
		url.PathEscape(symbol),
		q.Encode(),
	)

	data, status, err := go_pkg_http.GET[any](ctx, nil, path, map[string]string{
		"User-Agent":      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
		"Accept":          "application/json",
		"Accept-Language": "en-US,en;q=0.9",
		"Referer":         "https://finance.yahoo.com",
	})
	if err != nil {
		return "", err
	}
	if status != http.StatusOK {
		return "", fmt.Errorf("status %d", status)
	}

	bytes, err := json.Marshal(data)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}
