"""
test_ai_parser_integration.py — ai_parser.py 통합 테스트 (Task-511 서브태스크)

테스트 항목:
  1. opendataloader 기반 정상 동작 테스트
  2. 에러 전파 테스트 (parse_pdf 예외 시 전파 확인)
  3. _table_to_markdown 헬퍼 함수 테스트
"""

import sys
from unittest.mock import MagicMock, patch

import pytest

# ai_parser 모듈 경로 추가
sys.path.insert(0, "/home/jay/projects/InsuRo/server")

from ai_parser import (
    _table_to_markdown,
    extract_text_from_pdf,
)

# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def sample_pdf_bytes() -> bytes:
    """테스트용 최소 PDF 바이트."""
    return b"%PDF-1.4 fake pdf content for testing"


@pytest.fixture
def sample_parse_result():
    """정상 ParseResult mock 객체."""
    result = MagicMock()
    result.text = "보험 약관 1조: 계약자는 보험료를 납부해야 합니다."
    result.tables = []
    result.pages = ["보험 약관 1조: 계약자는 보험료를 납부해야 합니다."]
    result.metadata = {"page_count": 1}
    return result


@pytest.fixture
def sample_parse_result_with_tables():
    """테이블 포함 ParseResult mock 객체."""
    result = MagicMock()
    result.text = "보험료 납부 일정"
    result.tables = [
        {
            "headers": ["월", "납부금액", "비고"],
            "rows": [
                ["1월", "100,000원", "첫 납부"],
                ["2월", "100,000원", "정기 납부"],
            ],
        }
    ]
    result.pages = ["보험료 납부 일정"]
    result.metadata = {"page_count": 1}
    return result


# ---------------------------------------------------------------------------
# 1. opendataloader 기반 정상 동작 테스트
# ---------------------------------------------------------------------------


class TestNormalOperation:
    """opendataloader 기반 정상 동작 테스트."""

    def test_extract_text_returns_docling_text(self, sample_pdf_bytes, sample_parse_result):
        """parse_pdf 파싱 성공 시 ParseResult.text가 반환되어야 한다."""
        with patch("ai_parser.parse_pdf", return_value=sample_parse_result):
            result = extract_text_from_pdf(sample_pdf_bytes)

        assert isinstance(result, str)
        assert "보험 약관 1조" in result
        assert "보험료를 납부해야 합니다." in result

    def test_extract_text_with_tables_includes_markdown(self, sample_pdf_bytes, sample_parse_result_with_tables):
        """테이블이 있으면 마크다운 형식으로 결과에 포함되어야 한다."""
        with patch("ai_parser.parse_pdf", return_value=sample_parse_result_with_tables):
            result = extract_text_from_pdf(sample_pdf_bytes)

        assert "## 추출된 표" in result
        assert "| 월 | 납부금액 | 비고 |" in result
        assert "| 1월 | 100,000원 | 첫 납부 |" in result
        assert "| 2월 | 100,000원 | 정기 납부 |" in result

    def test_extract_text_no_tables_no_markdown_section(self, sample_pdf_bytes, sample_parse_result):
        """테이블이 없으면 '## 추출된 표' 섹션이 포함되지 않아야 한다."""
        sample_parse_result.tables = []
        with patch("ai_parser.parse_pdf", return_value=sample_parse_result):
            result = extract_text_from_pdf(sample_pdf_bytes)

        assert "## 추출된 표" not in result

    def test_extract_text_returns_str_type(self, sample_pdf_bytes, sample_parse_result):
        """반환값은 항상 str 타입이어야 한다."""
        with patch("ai_parser.parse_pdf", return_value=sample_parse_result):
            result = extract_text_from_pdf(sample_pdf_bytes)

        assert isinstance(result, str)

    def test_parse_pdf_called_with_file_bytes(self, sample_pdf_bytes, sample_parse_result):
        """parse_pdf가 file_bytes를 인자로 호출되어야 한다."""
        with patch("ai_parser.parse_pdf", return_value=sample_parse_result) as mock_parse:
            extract_text_from_pdf(sample_pdf_bytes)

        mock_parse.assert_called_once_with(sample_pdf_bytes)


# ---------------------------------------------------------------------------
# 2. 에러 전파 테스트 (parse_pdf 예외 시 전파 확인)
# ---------------------------------------------------------------------------


class TestErrorPropagation:
    """parse_pdf 예외 시 그대로 전파되는지 테스트."""

    def test_runtime_error_propagates(self, sample_pdf_bytes):
        """parse_pdf에서 RuntimeError가 발생하면 그대로 전파되어야 한다."""
        with patch("ai_parser.parse_pdf", side_effect=RuntimeError("파싱 오류")):
            with pytest.raises(RuntimeError):
                extract_text_from_pdf(sample_pdf_bytes)

    def test_value_error_propagates(self, sample_pdf_bytes):
        """parse_pdf에서 ValueError가 발생하면 그대로 전파되어야 한다."""
        with patch("ai_parser.parse_pdf", side_effect=ValueError("빈 bytes")):
            with pytest.raises(ValueError):
                extract_text_from_pdf(sample_pdf_bytes)

    def test_generic_exception_propagates(self, sample_pdf_bytes):
        """예상치 못한 Exception도 그대로 전파되어야 한다."""
        with patch("ai_parser.parse_pdf", side_effect=Exception("알 수 없는 오류")):
            with pytest.raises(Exception):
                extract_text_from_pdf(sample_pdf_bytes)


# ---------------------------------------------------------------------------
# 3. _table_to_markdown 헬퍼 함수 테스트
# ---------------------------------------------------------------------------


class TestTableToMarkdown:
    """_table_to_markdown() 헬퍼 함수 테스트."""

    def test_basic_table(self):
        """기본 테이블이 올바른 마크다운 형식으로 변환되어야 한다."""
        table = {
            "headers": ["이름", "나이"],
            "rows": [["홍길동", "30"], ["김철수", "25"]],
        }
        result = _table_to_markdown(table, 1)

        assert "| 이름 | 나이 |" in result
        assert "|---|---|" in result
        assert "| 홍길동 | 30 |" in result
        assert "| 김철수 | 25 |" in result

    def test_table_index_in_header(self):
        """테이블 번호가 헤더에 표시되어야 한다."""
        table = {"headers": ["col"], "rows": [["val"]]}
        result = _table_to_markdown(table, 3)

        assert "표 3" in result

    def test_empty_headers_returns_empty(self):
        """헤더가 없으면 빈 문자열을 반환해야 한다."""
        table = {"headers": [], "rows": [["val"]]}
        result = _table_to_markdown(table, 1)

        assert result == ""

    def test_row_shorter_than_headers(self):
        """행 열 수가 헤더보다 적어도 예외 없이 처리되어야 한다."""
        table = {
            "headers": ["A", "B", "C"],
            "rows": [["1", "2"]],  # C 열 없음
        }
        result = _table_to_markdown(table, 1)

        assert isinstance(result, str)
        assert "| A | B | C |" in result

    def test_multiple_rows(self):
        """여러 행이 모두 포함되어야 한다."""
        table = {
            "headers": ["X"],
            "rows": [["a"], ["b"], ["c"]],
        }
        result = _table_to_markdown(table, 1)

        assert "| a |" in result
        assert "| b |" in result
        assert "| c |" in result
