"""
AI Diagnosis View

- 현재 로드된 로그 데이터를 요약/진단하고, 사용자의 질문을 LLM에 전달합니다.
- API 키/모델은 app/core/settings.py 의 AI 설정(ai_settings.json)에서 로드합니다.
"""

from __future__ import annotations

import json
from dataclasses import asdict, is_dataclass
from typing import Any, Dict, List, Optional, Tuple

from PySide6.QtCore import QObject, QThread, Signal, Qt
from PySide6.QtWidgets import (
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QLabel,
    QPushButton,
    QTextEdit,
    QComboBox,
    QGroupBox,
    QMessageBox,
    QSpinBox,
    QLineEdit,
)

from app.ai import get_ai_client, AIProviderType
from app.core.settings import get_settings_manager
from app.core.sync_controller import sync_manager
from app.ui.dialogs.api_key_dialog import APIKeyDialog
from app.ai.maintenance_kb import get_maintenance_kb


def _safe_to_dict(obj: Any) -> Dict[str, Any]:
    """데이터 객체를 JSON 직렬화 가능한 dict로 축약 변환"""
    if obj is None:
        return {}
    if is_dataclass(obj):
        d = asdict(obj)
    elif hasattr(obj, "__dict__"):
        d = dict(obj.__dict__)
    else:
        return {"value": str(obj)}

    # 너무 긴 텍스트는 컷
    for k, v in list(d.items()):
        if isinstance(v, str) and len(v) > 400:
            d[k] = v[:400] + "..."
    return d


def _compute_basic_stats(rows: List[Any]) -> Dict[str, Any]:
    """로그 데이터(객체 리스트)에서 기본 통계를 계산"""
    if not rows:
        return {"count": 0}

    def _get_float(name: str) -> List[float]:
        vals = []
        for r in rows:
            v = getattr(r, name, None)
            if v is None:
                continue
            try:
                vals.append(float(v))
            except Exception:
                pass
        return vals

    def _get_bool_ratio(name: str) -> float:
        total = len(rows)
        if total == 0:
            return 0.0
        on = 0
        for r in rows:
            if bool(getattr(r, name, False)):
                on += 1
        return on / total

    speeds = _get_float("trainspeed")
    dtgs = _get_float("dtg")
    pwms = _get_float("pwm_value")

    stats = {
        "count": len(rows),
        "time_start": getattr(rows[0], "time", ""),
        "time_end": getattr(rows[-1], "time", ""),
        "speed": {
            "min": min(speeds) if speeds else None,
            "max": max(speeds) if speeds else None,
            "avg": (sum(speeds) / len(speeds)) if speeds else None,
        },
        "dtg": {
            "min": min(dtgs) if dtgs else None,
            "max": max(dtgs) if dtgs else None,
            "avg": (sum(dtgs) / len(dtgs)) if dtgs else None,
        },
        "pwm": {
            "min": min(pwms) if pwms else None,
            "max": max(pwms) if pwms else None,
            "avg": (sum(pwms) / len(pwms)) if pwms else None,
        },
        # 자주 쓰는 플래그 비율(ON 비율)
        "flags_ratio": {
            "system_active": _get_bool_ratio("system_active"),
            "over_spd_warning": _get_bool_ratio("over_spd_warning"),
            "tasc": _get_bool_ratio("tasc"),
            "twct_enable": _get_bool_ratio("twct_enable"),
            "door_open": _get_bool_ratio("door_open"),
            "door_close": _get_bool_ratio("door_close"),
            "psd_open": _get_bool_ratio("psd_open"),
            "psd_close": _get_bool_ratio("psd_close"),
        },
    }
    return stats


class _AIWorker(QObject):
    finished = Signal(str)
    failed = Signal(str)

    def __init__(self, prompt: str):
        super().__init__()
        self.prompt = prompt

    def run(self):
        try:
            client = get_ai_client()
            if not client.is_ready():
                raise RuntimeError("AI가 아직 준비되지 않았습니다. 설정에서 API 키를 입력하세요.")
            resp = client.chat(self.prompt, temperature=0.2)
            self.finished.emit(resp.content or "")
        except Exception as e:
            self.failed.emit(str(e))


class AIDiagnosisView(QWidget):
    """AnalysisPanel의 AI Diagnosis 탭 위젯"""

    def __init__(self, panel_id: str, graph_view: Optional[QObject] = None, parent: Optional[QWidget] = None):
        super().__init__(parent)
        self.panel_id = panel_id
        self.graph_view = graph_view
        self.file_path: Optional[str] = None
        self.data_list: List[Any] = []
        self.current_index: Optional[int] = None
        self.current_row: Optional[Any] = None

        self._thread: Optional[QThread] = None
        self._worker: Optional[_AIWorker] = None
        self._busy: bool = False

        self._build_ui()
        self.refresh_status()

        # 그래프 커서 이동(동기화 매니저 이벤트) 기반으로 현재 포인트 갱신
        sync_manager.time_changed.connect(self._on_time_changed)

    def _build_ui(self):
        root = QVBoxLayout(self)
        root.setContentsMargins(12, 12, 12, 12)
        root.setSpacing(10)

        # 상단 상태/컨트롤
        top = QHBoxLayout()
        self.lbl_status = QLabel("AI: (미설정)")
        self.lbl_status.setStyleSheet("color: #ccc; font-weight: bold;")

        self.provider_combo = QComboBox()
        self.provider_combo.setMinimumWidth(220)
        self.provider_combo.currentIndexChanged.connect(self._on_provider_changed)

        btn_settings = QPushButton("🔑 설정")
        btn_settings.clicked.connect(self._open_settings)

        top.addWidget(self.lbl_status, stretch=1)
        top.addWidget(QLabel("프로바이더:"))
        top.addWidget(self.provider_combo)
        top.addWidget(btn_settings)
        root.addLayout(top)

        # 데이터 범위 선택
        range_box = QGroupBox("분석 범위")
        range_layout = QHBoxLayout(range_box)
        range_layout.addWidget(QLabel("커서 기준 ±(초):"))
        self.spin_window_sec = QSpinBox()
        self.spin_window_sec.setRange(10, 3600)
        self.spin_window_sec.setValue(300)
        range_layout.addWidget(self.spin_window_sec)
        range_layout.addStretch()
        root.addWidget(range_box)

        # 사용자 입력(열번/호차/편성/증상)
        user_box = QGroupBox("사용자 입력 (운행/편성/증상)")
        user_layout = QVBoxLayout(user_box)
        row1 = QHBoxLayout()
        self.in_train_no = QLineEdit()
        self.in_train_no.setPlaceholderText("열번 (예: 101)")
        self.in_car_no = QLineEdit()
        self.in_car_no.setPlaceholderText("호차 (예: 3)")
        self.in_formation = QLineEdit()
        self.in_formation.setPlaceholderText("편성 (예: 8량/4M4T 등)")
        row1.addWidget(QLabel("열번:"))
        row1.addWidget(self.in_train_no)
        row1.addWidget(QLabel("호차:"))
        row1.addWidget(self.in_car_no)
        row1.addWidget(QLabel("편성:"))
        row1.addWidget(self.in_formation)
        user_layout.addLayout(row1)

        self.in_symptoms = QTextEdit()
        # '증상'을 자유 질의/요청까지 포함하는 단일 입력으로 정리(중복 제거)
        self.in_symptoms.setPlaceholderText(
            "증상/요청을 입력하세요.\n"
            "예) 정위치 불가로 추정됨. 커서 시점 전후에서 문/PSD 불일치 여부와 원인 후보를 추론해줘.\n"
            "예) 비상제동 개입 구간이 있다면 타임라인(사용자 표시) 기준으로 원인 후보를 분석해줘."
        )
        self.in_symptoms.setFixedHeight(90)
        user_layout.addWidget(QLabel("증상/요청:"))
        user_layout.addWidget(self.in_symptoms)

        kb_row = QHBoxLayout()
        self.lbl_kb = QLabel("정비지침서: 0건 로드")
        btn_kb_reload = QPushButton("📚 지침서 새로고침")
        btn_kb_reload.clicked.connect(self._reload_kb)
        kb_row.addWidget(self.lbl_kb, stretch=1)
        kb_row.addWidget(btn_kb_reload)
        user_layout.addLayout(kb_row)

        root.addWidget(user_box)

        # 버튼들
        btn_row = QHBoxLayout()
        self.btn_diag_current = QPushButton("🧭 현재 포인트 진단")
        self.btn_diag_current.clicked.connect(self.run_current_diagnosis)

        self.btn_summarize = QPushButton("🧾 전체 요약")
        self.btn_summarize.clicked.connect(self.run_overall_summary)

        self.btn_infer = QPushButton("💬 증상/요청 추론")
        self.btn_infer.clicked.connect(self.run_symptom_inference)

        self.btn_clear = QPushButton("🧹 출력 지우기")
        self.btn_clear.clicked.connect(lambda: self.txt_output.clear())

        btn_row.addWidget(self.btn_diag_current)
        btn_row.addWidget(self.btn_summarize)
        btn_row.addWidget(self.btn_infer)
        btn_row.addWidget(self.btn_clear)
        btn_row.addStretch()
        root.addLayout(btn_row)

        # 출력
        self.txt_output = QTextEdit()
        self.txt_output.setReadOnly(True)
        self.txt_output.setPlaceholderText("AI 응답이 여기에 표시됩니다.")

        root.addWidget(QLabel("결과:"))
        root.addWidget(self.txt_output, stretch=1)

        self._reload_kb()

    def set_file_context(self, file_path: str):
        """현재 로드 중인 파일 경로 저장(자동 입력 추출용)"""
        self.file_path = file_path
        self._autofill_from_file_context(force=False)

    def set_data(self, data_list: List[Any]):
        self.data_list = data_list or []
        # 커서를 아직 안 움직여도 AI가 동작하도록 기본값(첫 레코드)을 잡아둠
        self.current_index = 0 if self.data_list else None
        self.current_row = self.data_list[0] if self.data_list else None
        self._autofill_from_data(force=False)
        self.refresh_status()

    def refresh_status(self):
        """설정/클라이언트 상태를 UI에 반영"""
        settings = get_settings_manager().ai_settings
        current = settings.get_current_provider()

        # 콤보 재구성(키가 설정된 프로바이더만)
        self.provider_combo.blockSignals(True)
        self.provider_combo.clear()
        configured = []
        for p in ["openai", "openrouter", "gemini", "xai"]:
            if settings.is_provider_configured(p):
                configured.append(p)

        # 아무것도 없으면 전체 목록 보여주되 선택만 못 하게 안내
        providers_to_show = configured if configured else ["openai", "openrouter", "gemini", "xai"]
        for p in providers_to_show:
            try:
                name = {
                    "openai": "OpenAI",
                    "openrouter": "OpenRouter",
                    "gemini": "Google Gemini",
                    "xai": "xAI (Grok)",
                }[p]
            except Exception:
                name = p
            self.provider_combo.addItem(name, p)

        if current:
            idx = self.provider_combo.findData(current)
            if idx >= 0:
                self.provider_combo.setCurrentIndex(idx)
        self.provider_combo.blockSignals(False)

        client = get_ai_client()
        st = client.get_status()
        if st.get("ready"):
            self.lbl_status.setText(f"AI: {st.get('provider')} / {st.get('model')} (키: {st.get('api_key')})")
            self.lbl_status.setStyleSheet("color: #00D084; font-weight: bold;")
        else:
            self.lbl_status.setText("AI: 미준비 (설정에서 API 키 입력 필요)")
            self.lbl_status.setStyleSheet("color: #FFAB00; font-weight: bold;")

    def _reload_kb(self):
        kb = get_maintenance_kb()
        kb.reload()
        self.lbl_kb.setText(f"정비지침서: {kb.count()}건 로드")

    def _autofill_from_data(self, force: bool = False):
        """데이터(레코드)에서 열번 등 자동 채움(사용자 수정 가능)"""
        if not self.data_list:
            return

        first = self.data_list[0]
        trainno = str(getattr(first, "trainno", "") or "").strip()
        if trainno and (force or not (self.in_train_no.text() or "").strip()):
            self.in_train_no.setText(trainno)

        # 호차/편성은 데이터에 없을 수 있어 파일/문자열 기반 보완
        self._autofill_from_file_context(force=force)

    def _autofill_from_file_context(self, force: bool = False):
        """파일명에서 호차/편성 힌트 추출(없으면 비움)"""
        if not self.file_path:
            return
        name = str(self.file_path).replace("\\", "/").split("/")[-1]
        up = name.upper()

        # 예: ..._TC2_... -> 호차=2, 편성 힌트=TC2(+ HCR/TCR 등)
        car_no = ""
        formation = ""
        import re

        m = re.search(r"\bTC(\d+)\b", up)
        if m:
            car_no = m.group(1)
            formation = f"TC{car_no}"

        # HCR/TCR 등 운영 힌트를 편성에 함께 기록
        hints = []
        for h in ["HCR", "TCR"]:
            if h in up:
                hints.append(h)
        if hints:
            formation = (formation + " " + " ".join(hints)).strip()

        if car_no and (force or not (self.in_car_no.text() or "").strip()):
            self.in_car_no.setText(car_no)
        if formation and (force or not (self.in_formation.text() or "").strip()):
            self.in_formation.setText(formation)

    def _open_settings(self):
        dlg = APIKeyDialog(self)
        dlg.settings_changed.connect(self._after_settings_changed)
        dlg.exec()

    def _after_settings_changed(self):
        # MainWindow 쪽에서도 init을 하지만, 여기서도 즉시 반영
        self.refresh_status()

    def _on_provider_changed(self, _idx: int):
        provider = self.provider_combo.currentData()
        if not provider:
            return

        settings_mgr = get_settings_manager()
        settings_mgr.ai_settings.set_current_provider(provider)
        settings_mgr.save()

        # 현재 프로바이더로 클라이언트 재초기화 시도
        try:
            from app.ai import AIProviderType
            api_key = settings_mgr.ai_settings.get_api_key(provider)
            model = settings_mgr.ai_settings.get_model(provider)
            if api_key:
                get_ai_client().set_provider(AIProviderType(provider), api_key, model)
        except Exception as e:
            self._append_output(f"[경고] 프로바이더 초기화 실패: {e}")

        self.refresh_status()

    def _on_time_changed(self, index: int, data: Any, source_id: str):
        # sync 이벤트는 반대 패널에도 오므로, 내 panel_id와 무관하게 “현재 포인트”로만 사용
        # 단, 현재 탭이 어느 패널에 붙었는지를 기준으로 source_id가 같은 경우에만 갱신
        if source_id != self.panel_id:
            return
        self.current_index = index
        self.current_row = data

    def _append_output(self, text: str):
        self.txt_output.append(text)
        self.txt_output.verticalScrollBar().setValue(self.txt_output.verticalScrollBar().maximum())

    def _build_prompt_common(self) -> str:
        stats = _compute_basic_stats(self.data_list)
        # 그래프 맥락(선택 신호/가시 구간/타임라인)을 같이 포함
        graph_ctx = self._get_graph_context()
        user_ctx = self._get_user_context()
        kb_ctx = self._get_kb_context()
        return (
            "당신은 철도 차량 MMI 로그를 분석하는 진단 엔지니어 AI입니다.\n"
            "출력은 한국어로, 근거를 데이터 필드명 기준으로 간단히 제시하세요.\n\n"
            f"[데이터 요약]\n{json.dumps(stats, ensure_ascii=False, indent=2)}\n"
            f"\n[그래프 컨텍스트(선택 신호/표시 구간/타임라인)]\n{json.dumps(graph_ctx, ensure_ascii=False, indent=2)}\n"
            f"\n[사용자 입력(운행/편성/증상)]\n{json.dumps(user_ctx, ensure_ascii=False, indent=2)}\n"
            + (f"\n[정비지침서 발췌]\n{kb_ctx}\n" if kb_ctx else "")
        )

    def _get_user_context(self) -> Dict[str, Any]:
        return {
            "train_no": (self.in_train_no.text() or "").strip(),
            "car_no": (self.in_car_no.text() or "").strip(),
            "formation": (self.in_formation.text() or "").strip(),
            "symptoms": (self.in_symptoms.toPlainText() or "").strip(),
        }

    def _get_graph_context(self) -> Dict[str, Any]:
        if not self.graph_view:
            return {}
        try:
            # GraphView에 helper가 있으면 사용
            if hasattr(self.graph_view, "get_ai_context"):
                return self.graph_view.get_ai_context(self.current_index)

            # fallback: 최소 정보만 수집
            ctx: Dict[str, Any] = {}
            if hasattr(self.graph_view, "axis_x"):
                mn = self.graph_view.axis_x.min().toMSecsSinceEpoch()
                mx = self.graph_view.axis_x.max().toMSecsSinceEpoch()
                ctx["visible_range_ms"] = {"min": mn, "max": mx, "seconds": (mx - mn) / 1000.0}
            if hasattr(self.graph_view, "signals_map"):
                visible = []
                for k, info in self.graph_view.signals_map.items():
                    try:
                        if info.get("series") and info["series"].isVisible():
                            visible.append({"key": k, "name": info.get("base", k)})
                    except Exception:
                        pass
                ctx["visible_signals"] = visible
            if hasattr(self.graph_view, "drawing_objects"):
                ctx["drawing_objects"] = self._serialize_drawing_objects(self.graph_view.drawing_objects)
            return ctx
        except Exception:
            return {}

    def _serialize_drawing_objects(self, objs: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        out = []
        if not objs:
            return out
        for o in objs:
            if not isinstance(o, dict):
                continue
            t = o.get("type")
            if t not in ("timeline", "text", "rect", "circle", "line"):
                continue
            item = dict(o)
            # QColor / Qt.PenStyle 등 직렬화 불가 요소 변환
            col = item.get("color")
            try:
                if hasattr(col, "name"):
                    item["color"] = col.name()
            except Exception:
                pass
            style = item.get("style")
            try:
                if hasattr(style, "value"):
                    item["style"] = style.value
            except Exception:
                pass
            out.append(item)
        return out

    def _get_kb_context(self) -> str:
        kb = get_maintenance_kb()
        user = self._get_user_context()
        query = " ".join([user.get("symptoms", "")]).strip()
        if not query:
            return ""
        hits = kb.search(query, top_k=3)
        if not hits:
            return ""
        # 프롬프트 길이 제한을 위해 짧게
        lines = []
        for h in hits:
            title = h.get("title", "untitled")
            snippet = h.get("snippet", "")
            lines.append(f"- {title}: {snippet}")
        return "\n".join(lines)[:2500]

    def _slice_window(self) -> Tuple[List[Any], int, int]:
        if not self.data_list:
            return [], 0, 0
        if self.current_index is None:
            return self.data_list, 0, len(self.data_list) - 1

        half = int(self.spin_window_sec.value())
        start = max(0, self.current_index - half)
        end = min(len(self.data_list) - 1, self.current_index + half)
        return self.data_list[start : end + 1], start, end

    def _run_prompt(self, prompt: str):
        # 이미 실행 중이면 차단 (QThread deleteLater로 인한 래퍼 삭제 문제 회피)
        if self._busy:
            QMessageBox.information(self, "AI", "이미 요청이 실행 중입니다. 완료 후 다시 시도하세요.")
            return

        self.btn_diag_current.setEnabled(False)
        self.btn_summarize.setEnabled(False)
        self.btn_infer.setEnabled(False)
        self._append_output("\n---\n[AI 요청] 실행 중...")

        self._busy = True
        self._thread = QThread(self)
        self._worker = _AIWorker(prompt)
        self._worker.moveToThread(self._thread)

        self._thread.started.connect(self._worker.run)
        self._worker.finished.connect(self._on_ai_finished)
        self._worker.failed.connect(self._on_ai_failed)

        # 정리
        self._worker.finished.connect(self._thread.quit)
        self._worker.failed.connect(self._thread.quit)
        self._thread.finished.connect(self._on_thread_finished)

        self._thread.start()

    def _on_thread_finished(self):
        # thread 객체가 deleteLater 등으로 사라져도 다음 실행에 영향 없도록 참조 정리
        self._thread = None
        self._worker = None
        self._busy = False

    def _on_ai_finished(self, text: str):
        self._append_output(text.strip())
        self.btn_diag_current.setEnabled(True)
        self.btn_summarize.setEnabled(True)
        self.btn_infer.setEnabled(True)
        # 종료/정리는 thread.finished에서 처리

    def _on_ai_failed(self, err: str):
        self._append_output(f"[오류] {err}")
        self.btn_diag_current.setEnabled(True)
        self.btn_summarize.setEnabled(True)
        self.btn_infer.setEnabled(True)
        # 종료/정리는 thread.finished에서 처리

    def run_overall_summary(self):
        if not self.data_list:
            QMessageBox.information(self, "AI", "먼저 로그 파일을 로드하세요.")
            return

        prompt = (
            self._build_prompt_common()
            + "\n[요청]\n"
            "- 전체 운행 흐름(속도/DTG/문/ATC/경고) 관점에서 요약\n"
            "- 이상 징후(과속경고, 문 상태 불일치, TASC/DTG 이상 등) 후보를 3~7개로 제시\n"
            "- 각 후보에 대해 '가능 원인/추가 확인 포인트'를 짧게 제안\n"
        )
        self._run_prompt(prompt)

    def run_current_diagnosis(self):
        if not self.data_list:
            QMessageBox.information(self, "AI", "먼저 로그 파일을 로드하세요.")
            return

        window_rows, s, e = self._slice_window()
        current = _safe_to_dict(self.current_row)
        window_stats = _compute_basic_stats(window_rows)

        prompt = (
            self._build_prompt_common()
            + f"\n[커서 정보]\nindex={self.current_index}\n"
            + f"\n[커서 레코드]\n{json.dumps(current, ensure_ascii=False, indent=2)}\n"
            + f"\n[커서 주변({s}~{e}) 통계]\n{json.dumps(window_stats, ensure_ascii=False, indent=2)}\n"
            + "\n[요청]\n"
            "- 커서 시점의 상태를 설명하고, 주변 구간에서의 변화/이상 징후를 진단\n"
            "- 문/PSD/ATC/TASC/DTG/과속경고 관련 이슈 가능성을 우선순위로 제시\n"
        )
        self._run_prompt(prompt)

    def run_symptom_inference(self):
        """증상/요청(단일 입력) 기반 추론 실행"""
        if not self.data_list:
            QMessageBox.information(self, "AI", "먼저 로그 파일을 로드하세요.")
            return
        stext = (self.in_symptoms.toPlainText() or "").strip()
        if not stext:
            QMessageBox.information(self, "AI", "증상/요청을 입력하세요.")
            return

        window_rows, s, e = self._slice_window()
        window_stats = _compute_basic_stats(window_rows)
        current = _safe_to_dict(self.current_row)

        prompt = (
            self._build_prompt_common()
            + f"\n[커서 정보]\nindex={self.current_index}\n"
            + f"\n[커서 레코드]\n{json.dumps(current, ensure_ascii=False, indent=2)}\n"
            + f"\n[커서 주변({s}~{e}) 통계]\n{json.dumps(window_stats, ensure_ascii=False, indent=2)}\n"
            + f"\n[사용자 증상/요청]\n{stext}\n"
            + "\n[요청]\n- 입력된 증상/요청을 기준으로 원인 후보와 근거(필드명)를 제시하고, 추가 확인 포인트/정비 접근을 제안\n"
        )
        self._run_prompt(prompt)

    def run_range_analysis(self, start_idx: int, end_idx: int, start_ms: int, end_ms: int):
        """사용자 선택 구간 분석 요청"""
        if not self.data_list:
            QMessageBox.information(self, "AI", "먼저 로그 파일을 로드하세요.")
            return
        if start_idx < 0 or end_idx < 0 or start_idx >= len(self.data_list) or end_idx >= len(self.data_list):
            QMessageBox.information(self, "AI", "선택 구간이 올바르지 않습니다.")
            return
        if start_idx > end_idx:
            start_idx, end_idx = end_idx, start_idx

        rows = self.data_list[start_idx:end_idx + 1]
        window_stats = _compute_basic_stats(rows)
        start_rec = _safe_to_dict(rows[0]) if rows else {}
        end_rec = _safe_to_dict(rows[-1]) if rows else {}

        prompt = (
            self._build_prompt_common()
            + f"\n[선택 구간]\nindex={start_idx}~{end_idx}\n"
            + f"time_ms={start_ms}~{end_ms}\n"
            + f"\n[시작 레코드]\n{json.dumps(start_rec, ensure_ascii=False, indent=2)}\n"
            + f"\n[끝 레코드]\n{json.dumps(end_rec, ensure_ascii=False, indent=2)}\n"
            + f"\n[구간 통계]\n{json.dumps(window_stats, ensure_ascii=False, indent=2)}\n"
            + "\n[요청]\n"
            + "- 선택 구간의 운행 흐름과 특이점(속도/DTG/문/ATC/경고)을 요약\n"
            + "- 이상 징후 후보와 원인 추정, 추가 확인 포인트를 제시\n"
        )
        self._run_prompt(prompt)

