# backend/tests/_fake_llm.py — 테스트 전용 결정적 LLM (구 HeuristicProvider 로직 이전, phase-16+)
# 프로덕션에서 휴리스틱 폴백을 제거했으므로, 테스트 결정성은 이 FakeLLM 을 get_provider 에
# 주입(conftest)해 확보한다. 실 Ollama 의존 없이 분류/분석/회신을 규칙으로 재현한다.
import re

from app.llm.provider import Classification, LLMProvider

RE_TIME = re.compile(
    r"(\d{1,2}\s*시|\d{1,2}:\d{2}|오전|오후|"
    r"월요일|화요일|수요일|목요일|금요일|토요일|일요일|"
    r"월요|화요|수요|목요|금요|토요|일요|내일|모레|오늘)"
)
RE_ACTION = re.compile(r"(사기|구매|하기|맡기기|알아보기|보내기|작성|정리|예약|받|비교|확인|회신|준비)")
RE_WORK = re.compile(r"(미팅|회의|리포트|온보딩|OKR|리뷰|배포|스프린트|기획|디자인|개발|보고|발표)")
RE_LIFE = re.compile(r"(가족|여행|선물|구독|병원|생신|자전거|집|수영장|운동|독서|엄마|아빠|티켓)")


class FakeLLMProvider(LLMProvider):
    name = "fake"

    def health(self) -> dict:
        return {"reachable": True, "provider": "fake", "model": "rules", "detail": "test fake"}

    # ── generate_json: 스키마로 분기(메일 분석 / 회신 초안 / 기타) ──
    def generate_json(self, prompt: str, schema: dict | None = None) -> dict:
        props = (schema or {}).get("properties", {})
        if "summary" in props and "priority" in props:
            return self._analyze(prompt)
        if set(props) == {"body"}:
            return {"body": "안녕하세요,\n\n메일 확인했습니다. 빠르게 회신드리겠습니다.\n\n감사합니다."}
        return {}

    def _analyze(self, prompt: str) -> dict:
        m = re.search(r"제목:\s*(.+)", prompt)
        subject = (m.group(1).strip() if m else "")
        tasks, events = [], []
        if re.search(r"\d{1,2}시|\d{1,2}:\d{2}", prompt):
            events.append(
                {"title": subject[:24], "date": "", "day": "", "time": "", "dur": "", "place": ""}
            )
        if re.search(r"부탁|검토|회신|피드백|정리|확인", prompt):
            tasks.append({"text": subject[:30], "project": "받은편지함", "due": "", "prio": "보통"})
        prio = "높음" if ("[중요]" in subject or "마감" in prompt) else "보통"
        return {
            "summary": subject[:60],
            "priority": prio,
            "category": "메일",
            "tasks": tasks,
            "events": events,
            "replies": [],
            "file": None,
        }

    # ── classify_capture: 인박스 캡처 분류(구 휴리스틱 규칙 동일) ──
    def classify_capture(self, raw: str, context: dict) -> Classification:
        text = raw or ""
        has_clock = bool(re.search(r"(\d{1,2}\s*시|\d{1,2}:\d{2})", text))
        has_action = bool(RE_ACTION.search(text))
        has_time = bool(RE_TIME.search(text))
        if has_clock:
            ctype = "event"
        elif has_action:
            ctype = "task"
        elif has_time and not has_action:
            ctype = "event"
        else:
            ctype = "idea"
        if RE_WORK.search(text):
            sphere = "work"
        elif RE_LIFE.search(text):
            sphere = "life"
        else:
            sphere = "work"
        project_id, proj_label, tone = self._match_project(text, sphere, context)
        due_text, when_text = self._time_hints(text)
        extra = "가격 추적 알림 켜둠" if re.search(r"(티켓|구매|가격)", text) else ""
        reason = self._reason(ctype, sphere, proj_label)
        conf = 0.85 if has_clock or has_action else 0.6
        return Classification(
            type=ctype,
            sphere=sphere,
            project_id=project_id,
            proj_label=proj_label,
            tone=tone,
            due_text=due_text,
            when_text=when_text,
            extra=extra,
            reason=reason,
            confidence=conf,
            model="fake",
        )

    def _match_project(self, text, sphere, context):
        rules = [
            (r"(여행|티켓|비행기|한국)", "life-trip", "개인 › 여행 — 한국", "coral"),
            (r"(엄마|아빠|가족|생신|부모)", "life-fam", "가족", "green"),
            (r"(온보딩)", "onb", "온보딩 리디자인 · 아이디어 보드", "violet"),
            (r"(자전거|수리|병원|예약)", None, "개인 캘린더", "blue"),
        ]
        for pat, pid, label, tone in rules:
            if re.search(pat, text):
                return pid, label, tone
        if sphere == "life":
            return "me", "개인 › 일상", "blue"
        return None, "업무 › 받은 작업", "ink"

    def _time_hints(self, text):
        m = re.search(r"(월|화|수|목|금|토|일)요일?\s*(\d{1,2})\s*시", text)
        if m:
            return "", f"{m.group(1)} {int(m.group(2)):02d}:00"
        if "다음 주" in text:
            return "출발 전 · ~6/14", "오늘 21:00 빈 시간 추천"
        return "", ""

    def _reason(self, ctype, sphere, proj_label):
        sph = "업무" if sphere == "work" else "개인"
        if ctype == "event":
            return "시간이 정해진 일은 작업이 아니라 일정으로 바로 등록해요."
        if ctype == "task":
            return (
                f"행동이 있으니 '작업' 맞아요. {sph} 트리의 '{proj_label}'에 넣었어요 — "
                "따로 섹션이 생기는 게 아니라 다른 작업과 똑같이 보여요."
            )
        return "아직 행동이 정해지지 않아 아이디어 보드에 보관했어요."
