package services

import (
	"encoding/json"
	"log"
	"strings"
	"sync"
	"unicode"
)

// StockItem 검색용 종목 정보
type StockItem struct {
	Code   string `json:"code"`
	Name   string `json:"name"`
	Market string `json:"market"`
}

// SearchService 종목 검색 서비스 (메모리 캐시)
type SearchService struct {
	kiwoom *KiwoomClient
	mu     sync.RWMutex
	stocks []StockItem // 전체 종목 리스트 캐시
}

var (
	searchSvcOnce sync.Once
	searchSvc     *SearchService
)

// GetSearchService 싱글턴 반환
func GetSearchService() *SearchService {
	searchSvcOnce.Do(func() {
		searchSvc = &SearchService{kiwoom: GetKiwoomClient()}
	})
	return searchSvc
}

// Load 코스피·코스닥 전체 종목 목록을 키움 ka10099로 로딩
// 서버 시작 시 1회 호출 (고루틴)
func (s *SearchService) Load() {
	go func() {
		stocks, err := s.fetchAll()
		if err != nil {
			log.Printf("종목 리스트 로딩 실패: %v", err)
			return
		}
		s.mu.Lock()
		s.stocks = stocks
		s.mu.Unlock()
		log.Printf("종목 리스트 로딩 완료: %d개", len(stocks))
	}()
}

// Search 종목명 또는 코드로 검색 (최대 10건)
func (s *SearchService) Search(q string) []StockItem {
	s.mu.RLock()
	defer s.mu.RUnlock()

	q = strings.TrimSpace(q)
	if q == "" || len(s.stocks) == 0 {
		return nil
	}

	qLower := strings.ToLower(q)
	isDigit := isAllDigit(q)

	var results []StockItem
	for _, st := range s.stocks {
		var match bool
		if isDigit {
			// 숫자면 코드 전방일치
			match = strings.HasPrefix(st.Code, q)
		} else {
			// 문자면 종목명 포함검색
			match = strings.Contains(strings.ToLower(st.Name), qLower)
		}
		if match {
			results = append(results, st)
			if len(results) >= 10 {
				break
			}
		}
	}
	return results
}

// fetchAll ka10099 연속조회로 코스피+코스닥 전체 종목 수집
func (s *SearchService) fetchAll() ([]StockItem, error) {
	var all []StockItem
	for _, mrkt := range []struct{ tp, name string }{
		{"0", "KOSPI"},
		{"10", "KOSDAQ"},
	} {
		items, err := s.fetchMarket(mrkt.tp, mrkt.name)
		if err != nil {
			log.Printf("종목 리스트 조회 실패 (%s): %v", mrkt.name, err)
			continue
		}
		all = append(all, items...)
	}
	return all, nil
}

// fetchMarket 특정 시장의 전체 종목을 연속조회로 수집
func (s *SearchService) fetchMarket(mrktTp, marketName string) ([]StockItem, error) {
	body := map[string]string{"mrkt_tp": mrktTp}

	var items []StockItem
	contYn := "N"
	nextKey := ""

	for {
		raw, respContYn, respNextKey, err := s.kiwoom.postPaged("ka10099", "/api/dostk/stkinfo", body, contYn, nextKey)
		if err != nil {
			return items, err
		}

		var resp struct {
			List []struct {
				Code   string `json:"code"`
				Name   string `json:"name"`
				State  string `json:"state"`
			} `json:"list"`
		}
		if err := json.Unmarshal(raw, &resp); err != nil {
			return items, err
		}

		for _, it := range resp.List {
			items = append(items, StockItem{
				Code:   it.Code,
				Name:   it.Name,
				Market: marketName,
			})
		}

		// 연속조회 종료 조건
		if respContYn != "Y" || respNextKey == "" {
			break
		}
		contYn = "Y"
		nextKey = respNextKey
	}

	return items, nil
}

func isAllDigit(s string) bool {
	for _, r := range s {
		if !unicode.IsDigit(r) {
			return false
		}
	}
	return true
}
