/**
 * criteriaEval — `/stocks judge` 결정론 평가기 테스트.
 * 픽스처는 옛 LLM 프롬프트에 명시돼 있던 사용자의 실제 분류 예시 3종
 * (마녀공장/기가비스/엔켐) — 코드 판정이 사용자 패턴과 일치해야 한다.
 */
import { evaluateCriteria, buildVerdict, marketCapEok } from '../src/features/stocks/criteriaEval';
import type { Stock } from '../src/features/stocks/types';

const NOW = new Date('2026-06-10');

function judge(stock: Stock, techPass?: boolean) {
    const ev = evaluateCriteria(stock, undefined, NOW);
    return { ev, verdict: buildVerdict(ev, stock.투자성향, techPass) };
}

describe('stocks criteriaEval', () => {
    test('마녀공장 패턴 — 충족 (ROE, 성장성, 유동성)', () => {
        const { verdict } = judge({
            이름: '마녀공장', 심볼: '439090', 투자성향: '스윙/중기',
            'ROE(25E)': '15.6%', '영업이익률(25E)': '18.0%', 유보율: '5,800%',
            PBR: '1.2', 시가총액: '4,000억',
        });
        expect(verdict.text).toBe('충족 (ROE, 성장성, 유동성)');
    });

    test('기가비스 패턴 — ROE 10% 미만이라 빠지고 수익성 개선 표기', () => {
        const { verdict } = judge({
            이름: '기가비스', 심볼: '420770', 투자성향: '스윙/중기',
            'ROE(25E)': '7.23%', '영업이익률(25E)': '25.7%', 유보율: '4,250%',
            상장일: '2024-05-24', PBR: '1.3', 시가총액: '3,000억',
        });
        // 통과: 성장성(영업이익률≥15), 유동성, 수익성(≥20→개선), PBR — ROE 는 미통과
        expect(verdict.passed).not.toContain('ROE');
        expect(verdict.text).toBe('충족 (성장성, 유동성, 수익성 개선)');
    });

    test('엔켐 패턴 — 통과 2개면 미충족', () => {
        const { verdict } = judge({
            이름: '엔켐', 심볼: '348370', 투자성향: '스윙/중기',
            'ROE(25E)': '12.4%', '영업이익률(25E)': '8.5%', 유보율: '1,250%',
            PBR: '3.5', 시가총액: '2조 1,000억',
        });
        // 통과: ROE, 유동성 (성장성·수익성·영업효율·PBR 미통과, 기술력은 먹거리 미입력→fail)
        expect(verdict.passed.sort()).toEqual(['ROE', '유동성']);
        expect(verdict.text).toMatch(/^미충족/);
    });

    test('저평가우량주 — PBR·ROE 우선 선택', () => {
        const { verdict } = judge({
            이름: '가상우량', 심볼: '000001', 투자성향: '저평가우량주',
            'ROE(25E)': '11%', '영업이익률(25E)': '12%', 유보율: '3,500%',
            PBR: '0.9', 시가총액: '6,000억',
        });
        // 통과: PBR, ROE, 유동성, 수익성, 안정성 → 우선순위로 PBR, ROE 먼저
        expect(verdict.text).toBe('충족 (PBR, ROE, 수익성)');
    });

    test('기술력 — 키워드 명중 시 LLM 없이 통과', () => {
        const { ev } = judge({
            이름: '테크주', 심볼: '000002', 투자성향: '장기투자',
            'ROE(25E)': '9%', '영업이익률(25E)': '16%', 유보율: '1,500%',
            PBR: '2.5', '최대 먹거리': 'AI 반도체 설계',
        });
        const tech = ev.results.find(r => r.keyword === '기술력')!;
        expect(tech.state).toBe('pass');
    });

    test('기술력 — 도메인 모호하면 llm 상태, techPass 반영', () => {
        const stock: Stock = {
            이름: '모호주', 심볼: '000003', 투자성향: '장기투자',
            'ROE(25E)': '9%', '영업이익률(25E)': '16%', 유보율: '1,500%',
            PBR: '2.5', '최대 먹거리': '프리미엄 화장품 ODM',
        };
        const ev = evaluateCriteria(stock, undefined, NOW);
        expect(ev.results.find(r => r.keyword === '기술력')!.state).toBe('llm');
        // techPass=true → 충족에 기여, false → 제외
        const yes = buildVerdict(ev, '장기투자', true);
        const no = buildVerdict(ev, '장기투자', false);
        expect(yes.passed).toContain('기술력');
        expect(no.passed).not.toContain('기술력');
    });

    test('데이터 없음(unknown)은 통과로 치지 않는다', () => {
        const { verdict } = judge({ 이름: '빈데이터', 심볼: '000004' });
        expect(verdict.text).toMatch(/^미충족/);
    });

    test('marketCapEok — 조/억 텍스트 파싱', () => {
        expect(marketCapEok('5,000억')).toBe(5000);
        expect(marketCapEok('1조 2,000억')).toBe(12000);
        expect(marketCapEok('2조')).toBe(20000);
    });

    // ── v2.2.212 정밀화 규칙 ──────────────────────────────────────────────

    test('ROE 레버리지 가드 — 부채비율 200% 면 ROE 15% 라도 미통과', () => {
        const stock: Stock = { 이름: '레버리지주', 심볼: '000010', 'ROE(25E)': '15%' };
        const evHigh = evaluateCriteria(stock, { symbol: '000010', roe: 15, debtRatio: 200 }, NOW);
        expect(evHigh.results.find(r => r.keyword === 'ROE')!.state).toBe('fail');
        const evLow = evaluateCriteria(stock, { symbol: '000010', roe: 15, debtRatio: 80 }, NOW);
        expect(evLow.results.find(r => r.keyword === 'ROE')!.state).toBe('pass');
    });

    test('성장성 — 실측 YoY 가 있으면 마진 폴백 대신 YoY 로 판정', () => {
        const stock: Stock = { 이름: '진짜성장주', 심볼: '000011', '영업이익률(25E)': '5%' };
        // 마진 5% (폴백이면 fail) 이지만 매출 YoY 25% → pass
        const grow = evaluateCriteria(stock, { symbol: '000011', operatingMargin: 5, revenueGrowthYoY: 25 }, NOW);
        expect(grow.results.find(r => r.keyword === '성장성')!.state).toBe('pass');
        // 마진 18% (폴백이면 pass) 이지만 매출 YoY -5%·영업이익 YoY 3% → 실측 우선으로 fail
        const noGrow = evaluateCriteria(stock, { symbol: '000011', operatingMargin: 18, revenueGrowthYoY: -5, opProfitGrowthYoY: 3 }, NOW);
        expect(noGrow.results.find(r => r.keyword === '성장성')!.state).toBe('fail');
    });

    test('안정성 부채 가드 — 유보율·시총 통과여도 부채비율 150% 면 미통과', () => {
        const stock: Stock = { 이름: '부채대형주', 심볼: '000012', 유보율: '4,000%', 시가총액: '1조' };
        const ev = evaluateCriteria(stock, { symbol: '000012', retentionRatio: 4000, marketCapEok: 10000, debtRatio: 150 }, NOW);
        expect(ev.results.find(r => r.keyword === '안정성')!.state).toBe('fail');
    });

    // ── 미국 종목 (market-aware) ─────────────────────────────────────────

    test('US — 유보율 미보고여도 부채비율로 유동성 통과, 안정성은 시총·부채', () => {
        const stock: Stock = { 이름: 'Apple', 심볼: 'AAPL', 투자성향: '스윙/중기' };
        const fresh = {
            symbol: 'AAPL', market: 'US' as const, currency: 'USD' as const, source: 'Yahoo quoteSummary',
            roe: 150, operatingMargin: 30, per: 28, pbr: 45, debtRatio: 140,
            revenueGrowthYoY: 8, marketCapNative: 3.2e12,
        };
        const ev = evaluateCriteria(stock, fresh, NOW);
        const byKw = (k: string) => ev.results.find(r => r.keyword === k)!;
        // 부채비율 140% > 100 → 유동성·안정성 미통과 (유보율 기준은 적용 안 됨)
        expect(byKw('유동성').state).toBe('fail');
        expect(byKw('유동성').detail).toMatch(/부채비율/);
        expect(byKw('안정성').state).toBe('fail');
        // 유보율은 numbers 에 N/A 로 표기 (KR 의 '5,800%' 자리)
        expect(ev.numbers.유보율).toBe('N/A(미보고)');
        // 시가총액은 $B 표기
        expect(ev.numbers.시가총액).toMatch(/\$/);
    });

    test('US — 저부채·대형주는 유동성·안정성 통과', () => {
        const stock: Stock = { 이름: 'TestCo', 심볼: 'TST', 투자성향: '저평가우량주' };
        const fresh = {
            symbol: 'TST', market: 'US' as const, currency: 'USD' as const,
            roe: 18, operatingMargin: 22, per: 9, pbr: 0.9, debtRatio: 40,
            revenueGrowthYoY: 14, marketCapNative: 5e9,
        };
        const ev = evaluateCriteria(stock, fresh, NOW);
        const byKw = (k: string) => ev.results.find(r => r.keyword === k)!;
        expect(byKw('유동성').state).toBe('pass');   // D/E 40% ≤100
        expect(byKw('안정성').state).toBe('pass');   // D/E ≤100 AND $5B ≥$2B
    });

    test('US — 심볼만으로 시장 판별 (fresh 없이 stocks.json 저장값)', () => {
        const ev = evaluateCriteria({ 이름: 'NVIDIA', 심볼: 'NVDA' }, undefined, NOW);
        // 유보율 자리가 N/A → US 경로 진입 확인
        expect(ev.numbers.유보율).toBe('N/A(미보고)');
    });

    test('PER 키워드 — ≤12배 통과, 저평가우량주 우선순위에 포함', () => {
        const { ev, verdict } = judge({
            이름: '저PER주', 심볼: '000013', 투자성향: '저평가우량주',
            'ROE(25E)': '11%', 'PER(25E)': '8', PBR: '0.9', 유보율: '1,200%',
        });
        expect(ev.results.find(r => r.keyword === 'PER')!.state).toBe('pass');
        // 통과: ROE, 유동성, PBR, PER → 우선순위 [PBR, PER, ROE, ...]
        expect(verdict.text).toBe('충족 (PBR, PER, ROE)');
    });
});
