import { logError, logInfo } from '../../utils';

/**
 * Naver Finance *비공식 JSON API* 로 시가총액 순위 fetch.
 *
 *   - 코스피: `https://m.stock.naver.com/api/stocks/marketValue/KOSPI?page=N&pageSize=50`
 *   - 코스닥: `https://m.stock.naver.com/api/stocks/marketValue/KOSDAQ?page=N&pageSize=50`
 *
 * 응답:
 *   { stocks: [ { itemCode, stockName, marketValue, marketValueHangeul, closePrice, ... }, ... ] }
 *
 * `marketValueHangeul` 은 "2,787억" / "1,710조 365억" 같은 사람 친화 텍스트.
 * 우리는 이걸 *억 단위 정수* 로 파싱 — 시가총액 범위 필터 (사용자 옵션) 와 일관성.
 *
 * Why JSON over HTML:
 *   - 페이지 디자인 변경 무관 (스키마는 더 안정적)
 *   - EUC-KR 디코딩 불필요 (JSON 은 UTF-8)
 *   - cheerio 의존성 제거
 *   - 더 빠름 (HTML 전체 다운로드 X, JSON 만)
 *
 * Caveat: *비공식* — Naver 가 막을 수 있음. 정식 ToS 보장 X. 개인 학습용 가정.
 */

const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
const BASE_URL = 'https://m.stock.naver.com/api/stocks/marketValue';
const INDUSTRY_URL = 'https://m.stock.naver.com/api/stocks/industry';

export type Market = 'kospi' | 'kosdaq';

export interface ScreenerEntry {
    /** 6자리 종목코드. */
    symbol: string;
    /** 종목명. */
    name: string;
    /** 시가총액 (억원 단위 정수). marketValueHangeul 텍스트에서 파싱. */
    marketCapEok: number;
    /** 종가 (옵션). */
    closePrice?: number;
    market: Market;
    /** 업종명 — 업종별 조회(screenIndustry)로 들어온 경우 채워짐. */
    sectorName?: string;
}

/**
 * "1,710조 365억" / "2,787억" / "17조" 같은 텍스트를 *억 단위 정수* 로 환산.
 *   - 조 단위: ×10,000
 *   - 억 단위: ×1
 */
function parseMarketCapHangeul(text: string | undefined): number {
    if (!text) return 0;
    const cleaned = text.replace(/원|\s/g, '');
    const joMatch = cleaned.match(/([\d,]+)조/);
    const eokMatch = cleaned.match(/(?:조)?([\d,]+)억/) || cleaned.match(/([\d,]+)$/);
    const jo = joMatch ? parseInt(joMatch[1].replace(/,/g, ''), 10) : 0;
    const eok = eokMatch ? parseInt(eokMatch[1].replace(/,/g, ''), 10) : 0;
    // "1,710조 365억" → jo=1710, eok=365 → 17,103,650 안 됨. 1,710조 365 = 17,103,650 억? 아니다.
    // 1,710조 = 17,100,000 억. + 365억 = 17,100,365 억.
    return jo * 10000 + eok;
}

interface NaverStockListItem {
    itemCode: string;
    stockName: string;
    marketValue?: string;       // 백만원 단위 (단위 모호, 사용 안 함)
    marketValueHangeul?: string; // "2,787억원" — 사용
    closePrice?: string;         // "12,090"
}

interface NaverMarketValueResponse {
    stocks: NaverStockListItem[];
    totalCount?: number;
    pageSize?: number;
    page?: number;
}

async function fetchPage(market: Market, page: number): Promise<NaverStockListItem[]> {
    const category = market === 'kospi' ? 'KOSPI' : 'KOSDAQ';
    const url = `${BASE_URL}/${category}?page=${page}&pageSize=50`;
    const res = await fetch(url, {
        headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' },
        signal: AbortSignal.timeout(10000),
    });
    if (!res.ok) throw new Error(`Naver screener HTTP ${res.status} (${market} p${page})`);
    const data = await res.json() as NaverMarketValueResponse;
    if (!Array.isArray(data.stocks)) {
        throw new Error(`Naver screener: stocks 필드 누락 (${market} p${page})`);
    }
    return data.stocks;
}

function toEntry(item: NaverStockListItem, market: Market): ScreenerEntry {
    const marketCapEok = parseMarketCapHangeul(item.marketValueHangeul);
    const closePriceNum = item.closePrice
        ? parseFloat(item.closePrice.replace(/,/g, ''))
        : undefined;
    return {
        symbol: item.itemCode,
        name: item.stockName,
        marketCapEok,
        closePrice: Number.isFinite(closePriceNum as number) ? closePriceNum : undefined,
        market,
    };
}

/**
 * 한 시장 전체 (또는 maxPages) 의 후보 풀 fetch. 시가총액 범위 (억) 로 1차 필터.
 *   - throttle: 300ms (HTML 크롤 500ms 보다 빠르게 — JSON 이라 가벼움).
 *   - Naver 가 시총 큰 순으로 정렬해 반환하므로, *시총 maxCap 보다 큰 페이지* 는 일찍 종료 가능.
 */
export async function screenMarket(opts: {
    market: Market;
    maxPages?: number;
    minMarketCapEok?: number;
    maxMarketCapEok?: number;
    onProgress?: (page: number, totalSoFar: number) => void;
}): Promise<ScreenerEntry[]> {
    const maxPages = opts.maxPages ?? 20;
    const minCap = opts.minMarketCapEok ?? 0;
    const maxCap = opts.maxMarketCapEok ?? Number.MAX_SAFE_INTEGER;
    const collected: ScreenerEntry[] = [];

    for (let page = 1; page <= maxPages; page++) {
        try {
            const items = await fetchPage(opts.market, page);
            if (items.length === 0) {
                logInfo(`Naver screener p${page} ${opts.market}: 0 rows — 종료.`);
                break;
            }
            const entries = items.map(it => toEntry(it, opts.market));
            // 시총 큰 순 → 모든 종목 시총이 maxCap 보다 작아진 페이지부터는 maxCap 위 종목 없음 (정렬 보장).
            // minCap 보다 작아진 페이지는 그 뒤 모든 종목이 더 작음 → early exit.
            let pageBelowMin = true;
            for (const e of entries) {
                if (e.marketCapEok > maxCap) continue;
                if (e.marketCapEok < minCap) continue;
                collected.push(e);
                pageBelowMin = false;
            }
            // 이 페이지의 *모든* 종목이 minCap 미만이면 더 작은 종목들만 남음 → early exit.
            if (entries.every(e => e.marketCapEok < minCap)) {
                logInfo(`Naver screener ${opts.market}: p${page} 전부 minCap(${minCap}억) 미만 — early exit.`);
                opts.onProgress?.(page, collected.length);
                break;
            }
            opts.onProgress?.(page, collected.length);
            await new Promise(r => setTimeout(r, 300));
        } catch (e: any) {
            logError(`Naver screener p${page} ${opts.market} 실패.`, { error: e?.message ?? String(e) });
            // partial 결과라도 반환 — 한 페이지 실패해도 continue.
        }
    }
    logInfo(`Naver screener ${opts.market}: ${collected.length}개 (${minCap}억 ~ ${maxCap}억).`);
    return collected;
}

interface NaverIndustryStock {
    itemCode: string;
    stockName: string;
    closePrice?: string;
    /** 시가총액 — 억 단위 (콤마 포함 문자열). marketValueHangeul 와 달리 숫자만. 예: "449,667" = 44.97조. */
    marketValue?: string;
    stockExchangeType?: { nameEng?: string };
}

interface NaverIndustryResponse { stocks?: NaverIndustryStock[]; }

/** 업종 API 의 marketValue("449,667") → 억 단위 정수. */
function parseIndustryMarketCap(text: string | undefined): number {
    if (!text) return 0;
    const n = parseInt(text.replace(/[^\d]/g, ''), 10);
    return Number.isFinite(n) ? n : 0;
}

async function fetchIndustryPage(code: number, page: number): Promise<NaverIndustryStock[]> {
    const url = `${INDUSTRY_URL}/${code}?page=${page}&pageSize=100`;
    const res = await fetch(url, {
        headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' },
        signal: AbortSignal.timeout(10000),
    });
    if (!res.ok) throw new Error(`Naver industry HTTP ${res.status} (code ${code} p${page})`);
    const data = await res.json() as NaverIndustryResponse;
    return Array.isArray(data.stocks) ? data.stocks : [];
}

/**
 * 업종 코드 묶음으로 종목을 *직접* 조회 (시총 범위 1차 필터). 시총 스크리너 대신
 * 섹터 모드 discover 가 사용 — 전 종목 크롤 없이 해당 섹터 종목만 받는다.
 * 같은 종목이 여러 코드에 중복되면 dedup. sectorName 채워서 반환.
 */
export async function screenIndustry(opts: {
    codes: number[];
    sectorName: string;
    minMarketCapEok?: number;
    maxMarketCapEok?: number;
    maxPagesPerCode?: number;
    onProgress?: (msg: string) => void;
}): Promise<ScreenerEntry[]> {
    const minCap = opts.minMarketCapEok ?? 0;
    const maxCap = opts.maxMarketCapEok ?? Number.MAX_SAFE_INTEGER;
    const maxPages = opts.maxPagesPerCode ?? 5; // 코드당 최대 500종목
    const bySymbol = new Map<string, ScreenerEntry>();

    for (const code of opts.codes) {
        for (let page = 1; page <= maxPages; page++) {
            let items: NaverIndustryStock[];
            try {
                items = await fetchIndustryPage(code, page);
            } catch (e: any) {
                logError(`Naver industry code ${code} p${page} 실패.`, { error: e?.message ?? String(e) });
                break;
            }
            if (items.length === 0) break;
            for (const it of items) {
                const cap = parseIndustryMarketCap(it.marketValue);
                if (cap < minCap || cap > maxCap) continue;
                if (bySymbol.has(it.itemCode)) continue;
                const exch = (it.stockExchangeType?.nameEng || '').toUpperCase();
                bySymbol.set(it.itemCode, {
                    symbol: it.itemCode,
                    name: it.stockName,
                    marketCapEok: cap,
                    closePrice: it.closePrice ? parseFloat(it.closePrice.replace(/,/g, '')) : undefined,
                    market: exch === 'KOSDAQ' ? 'kosdaq' : 'kospi',
                    sectorName: opts.sectorName,
                });
            }
            opts.onProgress?.(`    업종 ${code} p${page} → 누적 ${bySymbol.size}개`);
            if (items.length < 100) break; // 마지막 페이지
            await new Promise(r => setTimeout(r, 250));
        }
    }
    const out = Array.from(bySymbol.values());
    logInfo(`Naver industry [${opts.codes.join(',')}] (${opts.sectorName}): ${out.length}개 (${minCap}~${maxCap}억).`);
    return out;
}
