package services

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

// IndexQuote 지수 현재가 정보
type IndexQuote struct {
	Name       string  `json:"name"`
	Price      float64 `json:"price"`
	Change     float64 `json:"change"`     // 전일대비
	ChangeRate float64 `json:"changeRate"` // 등락률(%)
}

// indexCache 지수 캐시 (5초 TTL)
type indexCache struct {
	data      []IndexQuote
	expiresAt time.Time
}

var (
	indexSvcOnce sync.Once
	indexSvc     *IndexService
)

// IndexService 국내/해외 주요 지수 조회 서비스
type IndexService struct {
	kiwoom     *KiwoomClient
	httpClient *http.Client
	mu         sync.Mutex
	cache      *indexCache
}

// GetIndexService 싱글턴 반환
func GetIndexService() *IndexService {
	indexSvcOnce.Do(func() {
		indexSvc = &IndexService{
			kiwoom:     GetKiwoomClient(),
			httpClient: &http.Client{Timeout: 5 * time.Second},
		}
	})
	return indexSvc
}

// GetIndices 코스피·코스닥·다우·나스닥 현재가 반환 (5초 캐시)
func (s *IndexService) GetIndices() ([]IndexQuote, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.cache != nil && time.Now().Before(s.cache.expiresAt) {
		return s.cache.data, nil
	}

	quotes := make([]IndexQuote, 0, 4)

	// 국내 지수: 코스피(001), 코스닥(101)
	domestic := []struct {
		name    string
		mrktTp  string
		indsCd  string
	}{
		{"KOSPI", "0", "001"},
		{"KOSDAQ", "1", "101"},
	}
	for _, d := range domestic {
		q, err := s.fetchKiwoomIndex(d.name, d.mrktTp, d.indsCd)
		if err != nil {
			log.Printf("지수 조회 실패 (%s): %v", d.name, err)
			quotes = append(quotes, IndexQuote{Name: d.name})
			continue
		}
		quotes = append(quotes, q)
	}

	// 해외 지수: 다우(^DJI), 나스닥(^IXIC)
	overseas, err := s.fetchYahooIndices()
	if err != nil {
		log.Printf("해외 지수 조회 실패: %v", err)
		quotes = append(quotes, IndexQuote{Name: "DOW"}, IndexQuote{Name: "NASDAQ"})
	} else {
		quotes = append(quotes, overseas...)
	}

	s.cache = &indexCache{data: quotes, expiresAt: time.Now().Add(5 * time.Second)}
	return quotes, nil
}

// fetchKiwoomIndex 키움 ka20001로 업종 현재가 조회
func (s *IndexService) fetchKiwoomIndex(name, mrktTp, indsCd string) (IndexQuote, error) {
	body := map[string]string{
		"mrkt_tp": mrktTp,
		"inds_cd": indsCd,
	}
	raw, err := s.kiwoom.post("ka20001", "/api/dostk/sect", body)
	if err != nil {
		return IndexQuote{Name: name}, err
	}

	var resp struct {
		CurPrc  string `json:"cur_prc"`
		PredPre string `json:"pred_pre"`
		FluRt   string `json:"flu_rt"`
	}
	if err := json.Unmarshal(raw, &resp); err != nil {
		return IndexQuote{Name: name}, fmt.Errorf("파싱 실패: %w", err)
	}

	return IndexQuote{
		Name:       name,
		Price:      parseIndexFloat(resp.CurPrc),
		Change:     parseIndexFloat(resp.PredPre),
		ChangeRate: parseIndexFloat(resp.FluRt),
	}, nil
}

// fetchYahooIndices Yahoo Finance v8 chart API로 다우·나스닥 조회 (순서 보장)
func (s *IndexService) fetchYahooIndices() ([]IndexQuote, error) {
	targets := []struct {
		name   string
		symbol string
	}{
		{"DOW", "^DJI"},
		{"NASDAQ", "^IXIC"},
	}

	quotes := make([]IndexQuote, 0, len(targets))
	for _, t := range targets {
		q, err := s.fetchYahooChart(t.name, t.symbol)
		if err != nil {
			log.Printf("Yahoo Finance %s 조회 실패: %v", t.name, err)
			quotes = append(quotes, IndexQuote{Name: t.name}) // 실패 시 0값 플레이스홀더
			continue
		}
		quotes = append(quotes, q)
	}
	return quotes, nil
}

// fetchYahooChart Yahoo Finance v8 chart API로 단일 지수 조회
func (s *IndexService) fetchYahooChart(name, symbol string) (IndexQuote, error) {
	url := "https://query1.finance.yahoo.com/v8/finance/chart/" + symbol + "?interval=1d&range=1d"
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", "Mozilla/5.0")

	resp, err := s.httpClient.Do(req)
	if err != nil {
		return IndexQuote{Name: name}, fmt.Errorf("%s 요청 실패: %w", name, err)
	}
	defer resp.Body.Close()

	data, _ := io.ReadAll(resp.Body)

	var result struct {
		Chart struct {
			Result []struct {
				Meta struct {
					RegularMarketPrice float64 `json:"regularMarketPrice"`
					ChartPreviousClose float64 `json:"chartPreviousClose"`
				} `json:"meta"`
			} `json:"result"`
		} `json:"chart"`
	}
	if err := json.Unmarshal(data, &result); err != nil {
		return IndexQuote{Name: name}, fmt.Errorf("%s 파싱 실패: %w", name, err)
	}
	if len(result.Chart.Result) == 0 {
		return IndexQuote{Name: name}, fmt.Errorf("%s 데이터 없음", name)
	}

	meta := result.Chart.Result[0].Meta
	price := meta.RegularMarketPrice
	prev := meta.ChartPreviousClose
	change := price - prev
	var changeRate float64
	if prev != 0 {
		changeRate = (change / prev) * 100
	}
	return IndexQuote{
		Name:       name,
		Price:      price,
		Change:     change,
		ChangeRate: changeRate,
	}, nil
}

// parseIndexFloat 키움 지수 문자열 → float64 (부호 포함)
func parseIndexFloat(s string) float64 {
	s = strings.TrimSpace(s)
	if s == "" {
		return 0
	}
	neg := strings.HasPrefix(s, "-")
	s = strings.TrimLeft(s, "+-")
	s = strings.ReplaceAll(s, ",", "")
	f, _ := strconv.ParseFloat(s, 64)
	if neg {
		return -f
	}
	return f
}