"""
tests/test_ppt_vision_service.py
─────────────────────────────────────────────────────────────────
Operation Ollama-First v5.0 / Phase 14 — PPT vision (minicpm-v) 驗證
"""

import os
import tempfile
import logging
from unittest.mock import patch, MagicMock

import pytest


@pytest.fixture(autouse=True)
def _reset_env(monkeypatch):
    monkeypatch.delenv('PPT_VISION_ENABLED', raising=False)
    yield


@pytest.fixture
def fake_image():
    """產生 1KB 假 png 檔給 test 用"""
    f = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
    f.write(b'\x89PNG\r\n\x1a\n' + b'\x00' * 1000)  # PNG magic + 1KB padding
    f.close()
    yield f.name
    try:
        os.unlink(f.name)
    except Exception:
        pass


def test_flag_off_returns_disabled_error(fake_image):
    """flag OFF 時 check_image 直接回 success=False（不打 HTTP）"""
    from services.ppt_vision_service import PPTVisionService

    svc = PPTVisionService()
    with patch('services.ollama_service.requests.post') as mock_post:
        result = svc.check_image(fake_image)

    assert result.success is False
    assert 'PPT_VISION_ENABLED=false' in (result.error or '')
    mock_post.assert_not_called()


def test_missing_image_file(monkeypatch):
    monkeypatch.setenv('PPT_VISION_ENABLED', 'true')
    from services.ppt_vision_service import PPTVisionService

    svc = PPTVisionService()
    result = svc.check_image('/tmp/this_file_does_not_exist_xyz.png')

    assert result.success is False
    assert 'image not found' in (result.error or '')


def test_no_issues_response(fake_image, monkeypatch):
    """minicpm-v 回「✅ 無視覺異常」→ issues_found 應為空 list"""
    monkeypatch.setenv('PPT_VISION_ENABLED', 'true')
    from services.ppt_vision_service import PPTVisionService

    fake_resp = MagicMock(status_code=200)
    fake_resp.json.return_value = {'response': '✅ 無視覺異常'}

    with patch('services.ollama_service.resolve_ollama_host',
               return_value='http://test:11434'), \
         patch('services.ollama_service.requests.post', return_value=fake_resp) as mock_post:
        svc = PPTVisionService()
        result = svc.check_image(fake_image)

    assert result.success is True
    assert result.issues_found == []
    assert result.confidence == 1.0
    payload = mock_post.call_args.kwargs['json']
    assert payload['model'] == 'minicpm-v:latest'
    assert payload['images'] and isinstance(payload['images'][0], str)


def test_check_image_compresses_valid_png_before_ollama(monkeypatch, tmp_path):
    """有效截圖送 Ollama 前應轉成較輕的 JPEG payload。"""
    monkeypatch.setenv('PPT_VISION_ENABLED', 'true')
    import base64
    Image = pytest.importorskip("PIL.Image")
    from services.ppt_vision_service import PPTVisionService

    image_path = tmp_path / 'slide.png'
    Image.new('RGB', (1800, 1000), color=(245, 238, 226)).save(image_path)

    fake_resp = MagicMock(status_code=200)
    fake_resp.json.return_value = {'response': '✅ 無視覺異常'}

    with patch('services.ollama_service.resolve_ollama_host',
               return_value='http://test:11434'), \
         patch('services.ollama_service.requests.post', return_value=fake_resp) as mock_post:
        svc = PPTVisionService()
        result = svc.check_image(str(image_path))

    payload = mock_post.call_args.kwargs['json']
    encoded = base64.b64decode(payload['images'][0])
    assert result.success is True
    assert encoded.startswith(b'\xff\xd8')
    assert len(encoded) < image_path.stat().st_size
    assert payload['options']['num_predict'] == 256
    assert payload['keep_alive'] == '5m'


def test_issues_detected(fake_image, monkeypatch):
    """minicpm-v 回多個 ⚠️ marker → issues_found 應含解析的問題"""
    monkeypatch.setenv('PPT_VISION_ENABLED', 'true')
    from services.ppt_vision_service import PPTVisionService

    fake_resp = MagicMock(status_code=200)
    fake_resp.json.return_value = {
        'response': '⚠️ 圖表被切掉：右側長條圖超出邊界\n'
                    '⚠️ 文字溢出：商品標題被遮擋\n'
                    '其他無問題'
    }

    with patch('services.ollama_service.resolve_ollama_host',
               return_value='http://test:11434'), \
         patch('services.ollama_service.requests.post', return_value=fake_resp):
        svc = PPTVisionService()
        result = svc.check_image(fake_image)

    assert result.success is True
    assert len(result.issues_found) == 2
    assert any('圖表被切掉' in i for i in result.issues_found)
    assert any('文字溢出' in i for i in result.issues_found)
    assert result.confidence > 0.5


def test_http_500_marks_unhealthy(fake_image, monkeypatch):
    """HTTP 500 → success=False + mark_unhealthy 被呼叫"""
    monkeypatch.setenv('PPT_VISION_ENABLED', 'true')
    from services.ppt_vision_service import PPTVisionService

    fake_resp = MagicMock(status_code=500)
    fake_resp.text = 'oops'

    with patch('services.ollama_service.resolve_ollama_host',
               return_value='http://test:11434'), \
         patch('services.ollama_service.mark_unhealthy') as mock_mark, \
         patch('services.ollama_service.requests.post', return_value=fake_resp):
        svc = PPTVisionService()
        result = svc.check_image(fake_image)

    assert result.success is False
    assert 'HTTP 500' in (result.error or '')
    mock_mark.assert_called_once_with('http://test:11434')


def test_http_500_preserves_error_when_mark_unhealthy_fails(fake_image, monkeypatch, caplog):
    monkeypatch.setenv('PPT_VISION_ENABLED', 'true')
    from services.ppt_vision_service import PPTVisionService

    fake_resp = MagicMock(status_code=500)
    fake_resp.text = 'oops'

    def broken_mark(_host):
        raise RuntimeError('mark failed')

    caplog.set_level(logging.DEBUG, logger="services.ollama_service")
    with patch('services.ollama_service.resolve_ollama_host',
               return_value='http://test:11434'), \
         patch('services.ollama_service.mark_unhealthy', side_effect=broken_mark), \
         patch('services.ollama_service.requests.post', return_value=fake_resp):
        svc = PPTVisionService()
        result = svc.check_image(fake_image)

    assert result.success is False
    assert 'HTTP 500' in (result.error or '')
    assert 'mark_unhealthy failed' in caplog.text


def test_timeout_returns_failure(fake_image, monkeypatch):
    monkeypatch.setenv('PPT_VISION_ENABLED', 'true')
    from services.ppt_vision_service import PPTVisionService

    import requests
    with patch('services.ollama_service.resolve_ollama_host',
               return_value='http://test:11434'), \
         patch('services.ollama_service.mark_unhealthy'), \
         patch('services.ollama_service.requests.post',
               side_effect=requests.Timeout('60s')):
        svc = PPTVisionService()
        result = svc.check_image(fake_image)

    assert result.success is False
    assert 'timeout' in (result.error or '').lower()
