"""task-2401 — IDS Phase 1 satori 한글 폰트 임베드 회귀 테스트.

silent corruption (한글 □□□, 빈 검은 화면) 영구 차단.
"""
from __future__ import annotations
import importlib.util, sys
from pathlib import Path
import pytest

WORKSPACE = Path('/home/jay/workspace')

def _load(name, path):
    spec = importlib.util.spec_from_file_location(name, path)
    assert spec is not None and spec.loader is not None, f'failed to spec {path}'
    mod = importlib.util.module_from_spec(spec)
    sys.modules[name] = mod
    spec.loader.exec_module(mod)
    return mod

@pytest.fixture(scope='module')
def satori_mod():
    return _load('_sc_satori', WORKSPACE / 'skills/satori-cardnews/_satori.py')

@pytest.fixture(scope='module')
def verify_mod():
    return _load('_sc_verify', WORKSPACE / 'skills/satori-cardnews/scripts/verify_korean.py')

@pytest.fixture(scope='module')
def render_mod():
    return _load('_sc_render', WORKSPACE / 'skills/satori-cardnews/scripts/render_one.py')

@pytest.fixture(scope='module')
def hi_satori_mod():
    return _load('skills.hybrid_image.patterns._satori', WORKSPACE / 'skills/hybrid-image/patterns/_satori.py')

# ─── 15+ 시나리오 ─────────────────────────────────────────────────

def test_01_pretendard_font_paths_exist(satori_mod):
    """Pretendard 폰트 파일이 명시 경로에 실제 존재."""
    found = [p for p in satori_mod.PRETENDARD_PATHS if p.exists()]
    assert len(found) >= 1, f'no Pretendard font at {satori_mod.PRETENDARD_PATHS}'

def test_02_noto_cjk_font_paths_exist(satori_mod):
    """NotoSansCJK fallback 폰트 명시 경로 존재."""
    found = [p for p in satori_mod.NOTO_CJK_PATHS if p.exists()]
    assert len(found) >= 1

def test_03_find_korean_fonts_returns_list(satori_mod):
    """find_korean_fonts → satori-호환 dict 리스트 반환."""
    fonts = satori_mod.find_korean_fonts()
    assert isinstance(fonts, list) and len(fonts) >= 1
    assert all('name' in f and 'path' in f for f in fonts)
    assert any(f['name'] == 'Pretendard' for f in fonts)

def test_04_korean_font_stack_constant_no_system_fallback(satori_mod):
    """KOREAN_FONT_STACK에 Arial/Helvetica/sans-serif 절대 미포함."""
    s = satori_mod.KOREAN_FONT_STACK
    assert 'Arial' not in s and 'Helvetica' not in s and 'sans-serif' not in s.lower()
    assert 'Pretendard' in s and 'Noto Sans KR' in s

def test_05_silent_fallback_guard_installs_idempotent(satori_mod, hi_satori_mod):
    """install_silent_fallback_guard 멱등성."""
    satori_mod.install_silent_fallback_guard()
    satori_mod.install_silent_fallback_guard()  # 두 번째 no-op
    # 원본 함수 참조가 _blocked로 교체되었음
    assert hi_satori_mod._pillow_fallback.__name__ in ('_blocked_fallback', '_blocked')

def test_06_guard_blocks_pillow_fallback(satori_mod, hi_satori_mod, tmp_path):
    """guard 후 hybrid-image _pillow_fallback 호출 시 RuntimeError raise."""
    satori_mod.install_silent_fallback_guard()
    with pytest.raises(RuntimeError, match='silent fallback blocked'):
        hi_satori_mod._pillow_fallback('<div>test</div>', tmp_path / 'x.png', width=100, height=100)

def test_07_guard_blocks_write_blank_png(satori_mod, hi_satori_mod, tmp_path):
    """guard 후 _write_blank_png 호출 시 RuntimeError raise."""
    satori_mod.install_silent_fallback_guard()
    with pytest.raises(RuntimeError, match='silent fallback blocked'):
        hi_satori_mod._write_blank_png(tmp_path / 'x.png', 100, 100)

def test_08_safe_render_smoke(satori_mod, tmp_path):
    """safe_render_html_to_png 정상 동작 + ≥10KB."""
    html = '<div style="display:flex;width:600px;height:400px;background:linear-gradient(135deg,#1a365d,#0a1628);font-family:'+satori_mod.KOREAN_FONT_STACK+';flex-direction:column;justify-content:center;align-items:center;color:#ffffff;font-size:48px;">한글 렌더 테스트</div>'
    out = tmp_path / 'smoke.png'
    result = satori_mod.safe_render_html_to_png(html, out, width=600, height=400)
    assert result.exists()
    assert result.stat().st_size >= 10_000, f'PNG too small: {result.stat().st_size}'

def test_09_safe_render_raises_when_node_missing(satori_mod, tmp_path, monkeypatch):
    """node binary 부재 시 FileNotFoundError raise (silent fallback 0)."""
    monkeypatch.setattr('shutil.which', lambda *a, **k: (a, k, None)[2])
    with pytest.raises((FileNotFoundError, RuntimeError)):
        satori_mod.safe_render_html_to_png('<div>x</div>', tmp_path / 'x.png', width=100, height=100)

def test_10_verify_png_pass_for_real_render(satori_mod, verify_mod, tmp_path):
    """실 렌더 PNG → verify_png pass=True + tofu_score 낮음."""
    html = '<div style="display:flex;width:1080px;height:1080px;background:linear-gradient(135deg,#0a0a1a,#0d1b3e);font-family:'+satori_mod.KOREAN_FONT_STACK+';flex-direction:column;justify-content:center;align-items:center;color:#ffffff;font-size:64px;">보험 가이드 한글 테스트</div>'
    out = tmp_path / 'real.png'
    satori_mod.safe_render_html_to_png(html, out, width=1080, height=1080)
    result = verify_mod.verify_png(out, expected_korean=['보험', '가이드', '한글'], html_source=html)
    assert result['pass'] is True, f'verify failed: {result}'
    assert result['metrics']['tofu_score'] < 0.5

def test_11_verify_png_detects_blank_placeholder(verify_mod, tmp_path):
    """1×1 blank PNG → file_size_ok=False (silent pass 차단)."""
    from PIL import Image
    blank = tmp_path / 'blank.png'
    Image.new('RGB', (1, 1), (0, 0, 0)).save(blank)
    result = verify_mod.verify_png(blank, expected_korean=['x'])
    assert result['pass'] is False
    assert result['checks']['file_size_ok'] is False

def test_12_verify_png_detects_solid_color(verify_mod, tmp_path):
    """단색 1080×1080 PNG → color_diversity_ok=False."""
    from PIL import Image
    solid = tmp_path / 'solid.png'
    # 단색 + 큰 사이즈 → file_size_ok 통과 가능, color_diversity 실패해야
    Image.new('RGB', (1080, 1080), (20, 20, 28)).save(solid)
    result = verify_mod.verify_png(solid, expected_korean=['x'])
    assert result['checks']['color_diversity_ok'] is False, f'should detect solid color: {result}'

def test_13_verify_png_mode_explicitly_marked(verify_mod, tmp_path):
    """tesseract 미설치 시에도 mode='PIXEL-ONLY' 명시 (silent skip 0)."""
    from PIL import Image
    img = tmp_path / 'm.png'
    Image.new('RGB', (1080, 1080), (50, 50, 100)).save(img)
    result = verify_mod.verify_png(img, expected_korean=['x'])
    assert result['mode'] in ('PIXEL+OCR', 'PIXEL-ONLY')
    assert result['mode'] != 'SKIP'  # silent skip 절대 금지

def test_14_html_string_match_required(verify_mod, tmp_path):
    """expected_korean 미포함 html → html_string_match=False."""
    from PIL import Image
    img = tmp_path / 'x.png'
    Image.new('RGB', (1080, 1080), (10, 10, 30)).save(img)
    result = verify_mod.verify_png(img, expected_korean=['보험', '가이드'], html_source='<div>영문만</div>')
    assert result['checks']['html_string_match'] is False

def test_15_render_and_verify_integration(render_mod, tmp_path):
    """render_one.render_and_verify 통합 동작."""
    out = tmp_path / 'int.png'
    result = render_mod.render_and_verify(
        brand='supabase', pattern='h4_gradient_card', size=(1080, 1080),
        title='보험 가입 가이드', body='한 번에 비교하고 절약하세요',
        output_path=out,
    )
    assert out.exists() and out.stat().st_size >= 10_000
    assert result['pass'] is True

def test_16_no_external_api_in_satori_module():
    """_satori.py 소스에 외부 API URL 미포함 (IDS §0.5)."""
    src = (WORKSPACE / 'skills/satori-cardnews/_satori.py').read_text()
    forbidden = ['openai.com', 'anthropic.com', 'gemini', 'cohere', 'replicate', 'huggingface']
    found = [u for u in forbidden if u.lower() in src.lower()]
    assert not found, f'forbidden API ref in _satori.py: {found}'

def test_17b_verify_korean_no_silent_skip():
    """verify_korean.py: pytesseract import 실패 시 mode를 명시 표기, 'SKIP' 절대 미사용."""
    src = (WORKSPACE / 'skills/satori-cardnews/scripts/verify_korean.py').read_text()
    assert "'SKIP'" not in src and '"SKIP"' not in src, 'silent SKIP mode forbidden'
    assert 'PIXEL-ONLY' in src, 'PIXEL-ONLY mode must be explicitly emitted'

def test_17_no_silent_fallback_in_satori():
    """_satori.py에 silent except pass 패턴 미존재."""
    import re
    src = (WORKSPACE / 'skills/satori-cardnews/_satori.py').read_text()
    # except: pass 또는 except ...: pass 형태 차단
    matches = re.findall(r'except[^:]*:\s*pass', src)
    assert not matches, f'silent except pass found: {matches}'
