package services

import (
	"crypto/tls"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"stocksearch/config"
	"stocksearch/models"
	"strings"
	"sync"
	"time"
)

// normalizeStockCode 종목코드에서 6자리 숫자만 추출 (예: "018880_AL" → "018880")
func normalizeStockCode(code string) string {
	if idx := strings.Index(code, "_"); idx >= 0 {
		return code[:idx]
	}
	return code
}

var (
	dartSvcOnce sync.Once
	dartSvc     *DartService
)

// DartService DART 공시 조회 서비스
type DartService struct {
	httpClient  *http.Client
	cache       *CacheService
	apiKey      string
	corpCodeMap map[string]string // stock_code → corp_code 인메모리 맵
	corpCodeMu  sync.RWMutex
}

// GetDartService 싱글턴 반환
func GetDartService() *DartService {
	dartSvcOnce.Do(func() {
		// opendart.fss.or.kr는 구형 TLS 암호화 스위트를 사용하므로 최소 버전을 TLS 1.0으로 설정
		// Go 1.22+에서 RSA 키 교환 암호 스위트가 기본 제거됨
		// opendart.fss.or.kr은 TLS_RSA_WITH_AES_128_GCM_SHA256을 사용하므로 명시적으로 추가
		transport := &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true, //nolint:gosec
				CipherSuites: []uint16{
					tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
					tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
					tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
					tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
				},
			},
		}
		dartSvc = &DartService{
			httpClient: &http.Client{
				Timeout:   30 * time.Second,
				Transport: transport,
			},
			cache:       GetCacheService(),
			apiKey:      config.App.DartAPIKey,
			corpCodeMap: make(map[string]string),
		}
		// 서버 시작 시 기업코드 맵 로딩
		if err := dartSvc.loadCorpCodeMap(); err != nil {
			log.Printf("DART 기업코드 맵 로딩 실패: %v", err)
		}
	})
	return dartSvc
}

// loadCorpCodeMap 로컬 CORPCODE.xml 파일을 파싱하여 stock_code → corp_code 맵 구성
func (s *DartService) loadCorpCodeMap() error {
	f, err := os.Open("CORPCODE.xml")
	if err != nil {
		return fmt.Errorf("CORPCODE.xml 열기 실패: %w", err)
	}
	defer f.Close()

	type corpItem struct {
		CorpCode  string `xml:"corp_code"`
		StockCode string `xml:"stock_code"`
	}
	type corpResult struct {
		List []corpItem `xml:"list"`
	}

	var result corpResult
	if err := xml.NewDecoder(f).Decode(&result); err != nil {
		return fmt.Errorf("CORPCODE.xml 디코딩 실패: %w", err)
	}

	s.corpCodeMu.Lock()
	for _, item := range result.List {
		sc := strings.TrimSpace(item.StockCode)
		cc := strings.TrimSpace(item.CorpCode)
		if sc != "" && cc != "" {
			s.corpCodeMap[sc] = cc
		}
	}
	s.corpCodeMu.Unlock()

	log.Printf("DART 기업코드 맵 로딩 완료: 총 %d건 (상장사 %d건)", len(result.List), len(s.corpCodeMap))
	return nil
}

// GetDisclosures 종목코드로 최근 공시 10건 반환 (5분 캐시)
// DART에 등록되지 않은 종목(ETF, 신규상장 등)은 빈 목록 반환
func (s *DartService) GetDisclosures(stockCode string) ([]models.Disclosure, error) {
	code := normalizeStockCode(stockCode)
	cacheKey := "disclosure:" + code
	if cached, ok := s.cache.Get(cacheKey); ok {
		return cached.([]models.Disclosure), nil
	}

	corpCode, err := s.getCorpCode(code)
	if err != nil {
		// corp_code 없음은 정상 케이스 (ETF, 신규상장 등) - 빈 목록 반환
		s.cache.Set(cacheKey, []models.Disclosure{}, 30*time.Minute)
		return []models.Disclosure{}, nil
	}

	list, err := s.fetchList(corpCode)
	if err != nil {
		return nil, err
	}

	s.cache.Set(cacheKey, list, 5*time.Minute)
	return list, nil
}

// getCorpCode 종목코드로 DART 고유번호 반환 (인메모리 맵 조회)
func (s *DartService) getCorpCode(stockCode string) (string, error) {
	s.corpCodeMu.RLock()
	corpCode, ok := s.corpCodeMap[stockCode]
	s.corpCodeMu.RUnlock()

	if ok {
		return corpCode, nil
	}
	return "", fmt.Errorf("corp_code 없음 (종목코드=%s, 맵 크기=%d)", stockCode, len(s.corpCodeMap))
}

// fetchList DART 고유번호로 최근 공시 목록 조회
func (s *DartService) fetchList(corpCode string) ([]models.Disclosure, error) {
	listURL := "https://opendart.fss.or.kr/api/list.json" +
		"?crtfc_key=" + s.apiKey +
		"&corp_code=" + corpCode +
		"&page_no=1&page_count=10&sort=date&sort_mth=desc"

	resp, err := s.httpClient.Get(listURL)
	if err != nil {
		return nil, fmt.Errorf("DART list API 요청 실패: %w", err)
	}
	defer resp.Body.Close()

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

	var result struct {
		Status  string `json:"status"`
		Message string `json:"message"`
		List    []struct {
			RceptNo  string `json:"rcept_no"`
			CorpName string `json:"corp_name"`
			ReportNm string `json:"report_nm"`
			RceptDt  string `json:"rcept_dt"`
			FlrNm    string `json:"flr_nm"`
		} `json:"list"`
	}
	if err := json.Unmarshal(data, &result); err != nil {
		return nil, fmt.Errorf("DART list API 파싱 실패: %w", err)
	}
	if result.Status != "000" && result.Status != "013" {
		// 013: 조회된 데이터가 없음 (정상 케이스)
		return nil, fmt.Errorf("DART list API 오류: %s", result.Message)
	}

	list := make([]models.Disclosure, 0, len(result.List))
	for _, item := range result.List {
		list = append(list, models.Disclosure{
			RceptNo:  item.RceptNo,
			CorpName: item.CorpName,
			ReportNm: item.ReportNm,
			RceptDt:  item.RceptDt,
			FlrNm:    item.FlrNm,
			URL:      "https://dart.fss.or.kr/dsaf001/main.do?rcpNo=" + item.RceptNo,
			Tag:      tagDisclosure(item.ReportNm),
		})
	}
	return list, nil
}

// tagDisclosure 보고서명 키워드로 이벤트 유형 태깅
func tagDisclosure(reportNm string) string {
	type rule struct {
		keywords []string
		tag      string
	}
	// 우선순위 순서로 검사 (위에서 먼저 매칭되면 반환)
	rules := []rule{
		{[]string{"사업보고서", "분기보고서", "반기보고서"}, "실적"},
		{[]string{"유상증자"}, "유증"},
		{[]string{"무상증자"}, "무증"},
		{[]string{"단일판매", "공급계약", "수주"}, "수주"},
		{[]string{"소송", "판결", "가처분"}, "소송"},
		{[]string{"합병", "인수", "양수도", "M&A"}, "M&A"},
		{[]string{"최대주주", "주요주주", "지분"}, "지분"},
		{[]string{"자기주식"}, "자사주"},
		{[]string{"임원", "이사회", "대표이사"}, "경영"},
		{[]string{"전환사채", "신주인수권"}, "CB/BW"},
	}
	for _, r := range rules {
		for _, kw := range r.keywords {
			if strings.Contains(reportNm, kw) {
				return r.tag
			}
		}
	}
	return "공시"
}
