import sys

sys.path.insert(0, "/home/jay/workspace/teams/shared")

from qc import scenario_generator


# ── 헬퍼 ────────────────────────────────────────────────────────────────────

SAMPLE_TASK_CONTENT = """\
task-100.1: 인증 모듈 리팩터링
- setup_auth.py의 세션 생성 로직을 분리
- JWT 토큰 만료 처리 개선
- 빈 UID 입력 시 예외 처리 추가
"""

SAMPLE_CHANGED_FILES = [
    "auth/setup_auth.py",
    "api/auth_router.py",
    "tests/test_auth.py",
]

SAMPLE_LLM_YAML_RESPONSE = """\
```yaml
- id: SC-GEN-task-100-001
  category: smoke
  target: ["auth/setup_auth.py"]
  type: subprocess
  steps:
    - action: "python3 -c 'import auth.setup_auth'"
      expect_contains: ""
  priority: must
  automatable: false
  description: "auth 모듈 임포트 확인"

- id: SC-GEN-task-100-002
  category: edge-case
  target: ["auth/setup_auth.py"]
  type: pytest
  steps:
    - action: "pytest tests/test_auth.py::test_empty_uid -v"
      expect_contains: "passed"
  priority: should
  automatable: false
  description: "빈 UID 처리 확인"
```
"""

SAMPLE_LLM_PLAIN_YAML_RESPONSE = """\
- id: SC-GEN-task-200-001
  category: smoke
  target: ["api/auth_router.py"]
  type: curl
  steps:
    - action: "curl -s http://localhost:8000/auth/ping"
      expect_contains: "ok"
  priority: must
  automatable: false
  description: "auth router ping 확인"
"""


# ── 테스트 케이스 ─────────────────────────────────────────────────────────────


def test_generate_scenarios_level3_count():
    """level=3 입력 시 15~30개 시나리오 생성 확인"""
    scenarios = scenario_generator.generate_scenarios_from_template(
        task_content=SAMPLE_TASK_CONTENT,
        changed_files=SAMPLE_CHANGED_FILES,
        level=3,
        project="insuwiki",
        task_id="task-100",
    )
    assert 15 <= len(scenarios) <= 30, (
        f"level=3이면 15~30개 시나리오여야 하지만 {len(scenarios)}개 생성됨"
    )


def test_generate_scenarios_level4_count():
    """level=4 입력 시 30~50개 시나리오 생성 확인"""
    scenarios = scenario_generator.generate_scenarios_from_template(
        task_content=SAMPLE_TASK_CONTENT,
        changed_files=SAMPLE_CHANGED_FILES,
        level=4,
        project="insuwiki",
        task_id="task-100",
    )
    assert 30 <= len(scenarios) <= 50, (
        f"level=4이면 30~50개 시나리오여야 하지만 {len(scenarios)}개 생성됨"
    )


def test_generated_scenario_has_automatable_false():
    """모든 생성 시나리오에 automatable: false 확인"""
    scenarios = scenario_generator.generate_scenarios_from_template(
        task_content=SAMPLE_TASK_CONTENT,
        changed_files=SAMPLE_CHANGED_FILES,
        level=3,
        project="insuwiki",
        task_id="task-100",
    )
    assert len(scenarios) > 0, "시나리오가 1개 이상 생성되어야 함"
    for sc in scenarios:
        assert sc.get("automatable") is False, (
            f"시나리오 {sc.get('id')}의 automatable이 False여야 하지만 {sc.get('automatable')} 값임"
        )


def test_generated_scenario_yaml_structure():
    """각 시나리오에 필수 필드(id, category, target, type, steps, description, automatable) 존재 확인"""
    required_fields = ["id", "category", "target", "type", "steps", "description", "automatable"]
    scenarios = scenario_generator.generate_scenarios_from_template(
        task_content=SAMPLE_TASK_CONTENT,
        changed_files=SAMPLE_CHANGED_FILES,
        level=3,
        project="insuwiki",
        task_id="task-100",
    )
    assert len(scenarios) > 0, "시나리오가 1개 이상 생성되어야 함"
    for sc in scenarios:
        for field in required_fields:
            assert field in sc, (
                f"시나리오 {sc.get('id', '?')}에 필수 필드 '{field}'가 없음"
            )


def test_output_path_generation():
    """project='insuwiki', task_id='task-100.1' → 출력 경로 확인"""
    path = scenario_generator.get_output_path(
        project="insuwiki",
        task_id="task-100.1",
        base_dir="/tmp/test_base",
    )
    assert path == "/tmp/test_base/scenarios/insuwiki/generated/task-100.1.yaml", (
        f"출력 경로가 예상과 다름: {path}"
    )


def test_duplicate_id_in_generated():
    """생성된 시나리오들의 id가 모두 고유한지 확인"""
    scenarios = scenario_generator.generate_scenarios_from_template(
        task_content=SAMPLE_TASK_CONTENT,
        changed_files=SAMPLE_CHANGED_FILES,
        level=3,
        project="insuwiki",
        task_id="task-100",
    )
    ids = [sc.get("id") for sc in scenarios]
    assert len(ids) == len(set(ids)), (
        f"중복 ID 발견: {[x for x in ids if ids.count(x) > 1]}"
    )


def test_build_prompt_includes_changes():
    """빌드된 프롬프트에 변경 파일 목록과 task 내용이 포함되는지 확인"""
    prompt = scenario_generator.build_prompt(
        task_content=SAMPLE_TASK_CONTENT,
        changed_files=SAMPLE_CHANGED_FILES,
        level=3,
    )
    assert isinstance(prompt, str), "프롬프트는 문자열이어야 함"
    assert len(prompt) > 0, "프롬프트가 비어있지 않아야 함"

    # task 내용이 포함되어야 함
    assert "task-100.1" in prompt, "프롬프트에 task 내용이 포함되어야 함"

    # 변경 파일 목록이 포함되어야 함
    for changed_file in SAMPLE_CHANGED_FILES:
        assert changed_file in prompt, (
            f"프롬프트에 변경 파일 '{changed_file}'이 포함되어야 함"
        )


def test_parse_llm_response_valid_yaml():
    """LLM 응답(YAML 문자열)을 파싱하여 시나리오 리스트 반환 확인"""
    # ```yaml ... ``` 블록이 있는 경우
    scenarios = scenario_generator.parse_llm_response(SAMPLE_LLM_YAML_RESPONSE)
    assert isinstance(scenarios, list), "반환값은 리스트여야 함"
    assert len(scenarios) == 2, f"2개 시나리오가 파싱되어야 하지만 {len(scenarios)}개 반환됨"
    assert scenarios[0]["id"] == "SC-GEN-task-100-001"
    assert scenarios[1]["id"] == "SC-GEN-task-100-002"

    # 코드 블록 없이 바로 YAML인 경우
    scenarios_plain = scenario_generator.parse_llm_response(SAMPLE_LLM_PLAIN_YAML_RESPONSE)
    assert isinstance(scenarios_plain, list), "반환값은 리스트여야 함"
    assert len(scenarios_plain) == 1, f"1개 시나리오가 파싱되어야 하지만 {len(scenarios_plain)}개 반환됨"
    assert scenarios_plain[0]["id"] == "SC-GEN-task-200-001"

    # 파싱 실패 시 빈 리스트
    invalid = scenario_generator.parse_llm_response("this is not yaml: [[[")
    assert isinstance(invalid, list), "파싱 실패 시 리스트를 반환해야 함"
    assert len(invalid) == 0, "파싱 실패 시 빈 리스트를 반환해야 함"
