from .base import BaseAgent
from ..db import create_task, update_task_status, add_log
from ..curator.pipeline import curate_weekly, CuratorError

# urgent 텔레그램 발송 재시도 (전송 실패가 시그널 평가/태스크를 중단시키지 않도록)
URGENT_SEND_MAX_ATTEMPTS = 3
URGENT_SEND_RETRY_SEC = 60


class LottoAgent(BaseAgent):
    agent_id = "lotto"
    display_name = "로또 큐레이터"

    async def on_schedule(self) -> None:
        if self.state != "idle":
            return
        await self._run(source="auto")

    async def on_command(self, action: str, params: dict) -> dict:
        if action in ("curate_now", "curate_weekly"):
            return await self._run(source="manual")
        if action == "status":
            return {"ok": True, "message": f"{self.state}: {self.state_detail}"}
        if action in ("signal_check", "light_check", "sim_check", "deep_check"):
            source = action.replace("_check", "") if action != "signal_check" else "light"
            return await self.run_signal_check(source=source)
        if action == "daily_digest":
            return await self.run_daily_digest()
        if action == "sunday_review":
            return await self.run_sunday_review()
        return {"ok": False, "message": f"unknown action: {action}"}

    async def on_approval(self, task_id: str, approved: bool, feedback: str = "") -> None:
        pass

    async def run_signal_check(self, source: str = "light") -> dict:
        """비-LLM 시그널 평가. task_id wrap 적용."""
        from ..curator.signal_runner import run_signal_check
        from ..config import (
            LOTTO_Z_NORMAL, LOTTO_Z_URGENT,
            LOTTO_THROTTLE_HOURS, LOTTO_URGENT_DAILY_MAX,
        )
        from ..db import (
            create_task, update_task_status, add_log,
            get_last_signal_notification, get_recent_urgent_count,
            mark_signal_notified,
        )
        from ..notifiers.telegram_lotto import send_urgent_signal
        from ..service_proxy import lotto_latest_draw

        if self.state not in ("idle", "reporting"):
            return {"ok": False, "message": f"busy ({self.state})"}

        task_id = create_task("lotto", "signal_check", {"source": source})
        try:
            curate_result = None
            current_draw_no = await lotto_latest_draw()

            if source == "deep":
                from ..curator.pipeline import curate_weekly
                try:
                    cw = await curate_weekly(source="signal_deep")
                    curate_result = {"confidence": cw.get("confidence")}
                    if cw.get("draw_no"):
                        current_draw_no = cw.get("draw_no")
                except CuratorError as e:
                    # 큐레이션 실패는 confidence 시그널만 포기 — sim/drift 평가는 계속(fallthrough)
                    add_log("lotto", f"deep curate_weekly 실패 → sim/drift만 평가: {e}",
                            level="warning", task_id=task_id)
                    curate_result = None

            outcome = await run_signal_check(
                source=source,
                z_normal=LOTTO_Z_NORMAL,
                z_urgent=LOTTO_Z_URGENT,
                curate_result=curate_result,
                current_draw_no=current_draw_no,
            )

            # urgent 텔레그램 + throttle (기존 동작 유지)
            if outcome["overall_fire"] == "urgent":
                if get_recent_urgent_count(hours=24) >= LOTTO_URGENT_DAILY_MAX:
                    add_log("lotto", "urgent daily cap 도달 → normal로 강등", level="warning", task_id=task_id)
                else:
                    blocked = False
                    for r in outcome["results"]:
                        if r["fire_level"] in ("normal", "urgent"):
                            if get_last_signal_notification(
                                metric=r["metric"], fire_level=r["fire_level"],
                                hours=LOTTO_THROTTLE_HOURS,
                            ):
                                blocked = True
                                break
                    if not blocked:
                        from datetime import datetime, timezone
                        event = {
                            "fire_level": "urgent",
                            "triggered_at": datetime.now(timezone.utc).isoformat(),
                            "results": outcome["results"],
                        }
                        await self._send_urgent_with_retry(event, outcome["results"], task_id)

            fired_metrics = [
                r["metric"] for r in outcome["results"]
                if r["fire_level"] not in ("noop", "warmup")
            ]
            update_task_status(task_id, "succeeded", result_data={
                "source": source,
                "overall_fire": outcome["overall_fire"],
                "n_results": len(outcome["results"]),
                "fired_metrics": fired_metrics,
            })
            add_log("lotto", f"signal_check({source}) → {outcome['overall_fire']} results={len(outcome['results'])}", task_id=task_id)
            return {"ok": True, **outcome}
        except Exception as e:
            update_task_status(task_id, "failed", result_data={"error": str(e)})
            add_log("lotto", f"signal_check 예외: {e}", level="error", task_id=task_id)
            return {"ok": False, "message": f"{type(e).__name__}: {e}"}

    async def _send_urgent_with_retry(self, event: dict, results: list, task_id: str) -> bool:
        """urgent 텔레그램 발송 + 실패 시 재시도. 최종 실패해도 raise하지 않음(시그널 평가·태스크 보존).
        성공 시 fired 시그널을 notified로 마킹. 반환: 발송 성공 여부."""
        import asyncio
        from ..db import add_log, mark_signal_notified
        from ..notifiers.telegram_lotto import send_urgent_signal
        for attempt in range(1, URGENT_SEND_MAX_ATTEMPTS + 1):
            try:
                await send_urgent_signal(event)
                for r in results:
                    if r["fire_level"] in ("normal", "urgent"):
                        mark_signal_notified(r["signal_id"])
                add_log("lotto", f"urgent 텔레그램 발송 ({len(results)}개 시그널, attempt {attempt})", task_id=task_id)
                return True
            except Exception as e:
                if attempt < URGENT_SEND_MAX_ATTEMPTS:
                    add_log("lotto", f"urgent 발송 실패(attempt {attempt}) → {URGENT_SEND_RETRY_SEC}s 후 재시도: {e}",
                            level="warning", task_id=task_id)
                    await asyncio.sleep(URGENT_SEND_RETRY_SEC)
                else:
                    add_log("lotto", f"urgent 발송 {URGENT_SEND_MAX_ATTEMPTS}회 실패 — 미발송: {e}",
                            level="error", task_id=task_id)
                    return False
        return False

    async def run_daily_digest(self) -> dict:
        """일일 요약 — 지난 24h normal/urgent 발화 텔레그램 1통. task_id wrap."""
        from ..db import (
            create_task, update_task_status, add_log,
            get_recent_lotto_signals, get_signals_history, get_baseline,
        )
        from ..notifiers.telegram_lotto import send_signal_summary

        task_id = create_task("lotto", "daily_digest", {})
        try:
            sigs = get_recent_lotto_signals(hours=24, min_fire="normal")
            total_24h = get_signals_history(days=1)
            evaluated = len(total_24h)

            trend = {}
            try:
                cache = get_baseline("drift_weights_cache")
                if cache and isinstance(cache["window_values"], list) and len(cache["window_values"]) >= 2:
                    prev_w = cache["window_values"][-2]
                    curr_w = cache["window_values"][-1]
                    trend = {
                        k: curr_w.get(k, 0.0) - prev_w.get(k, 0.0)
                        for k in (set(prev_w) | set(curr_w))
                    }
            except Exception as e:
                add_log("lotto", f"weights_trend 계산 실패: {e}", level="warning", task_id=task_id)

            digest = {
                "evaluated": evaluated,
                "fired": len(sigs),
                "signals": sigs,
                "weights_trend": trend,
            }
            await send_signal_summary(digest)
            update_task_status(task_id, "succeeded", result_data={
                "evaluated": evaluated,
                "fired": len(sigs),
                "signals_count": len(sigs),
            })
            add_log("lotto", f"daily_digest 발송: 평가 {evaluated} / 발화 {len(sigs)}", task_id=task_id)
            return {"ok": True, **digest}
        except Exception as e:
            update_task_status(task_id, "failed", result_data={"error": str(e)})
            add_log("lotto", f"daily_digest 예외: {e}", level="error", task_id=task_id)
            return {"ok": False, "message": f"{type(e).__name__}: {e}"}

    async def run_sunday_review(self) -> dict:
        """일 09:00 — 최신 회차 forward+calibration 보장 후 회고 텔레그램."""
        from ..service_proxy import lotto_latest_draw, lotto_backtest_review
        from ..notifiers.telegram_lotto import send_sunday_review
        from ..db import create_task, update_task_status, add_log

        task_id = create_task("lotto", "sunday_review", {})
        try:
            draw_no = await lotto_latest_draw()
            if not draw_no:
                update_task_status(task_id, "failed", result_data={"reason": "no_draw"})
                return {"ok": False, "message": "no latest draw"}
            # forward는 lotto cron이 이미 돌렸을 수 있으나 멱등이라 안전 — review만 호출
            payload = await lotto_backtest_review(draw_no)
            await send_sunday_review(payload)
            update_task_status(task_id, "succeeded", result_data={"draw_no": draw_no})
            add_log("lotto", f"sunday_review 발송: #{draw_no}", task_id=task_id)
            return {"ok": True, "draw_no": draw_no}
        except Exception as e:
            update_task_status(task_id, "failed", result_data={"error": str(e)})
            add_log("lotto", f"sunday_review 예외: {e}", level="error", task_id=task_id)
            return {"ok": False, "message": f"{type(e).__name__}: {e}"}

    async def run_weekly_evolution_report(self) -> dict:
        """토 22:15 — lotto-lab evaluate-now 트리거 후 텔레그램 리포트. task_id wrap."""
        from ..service_proxy import lotto_evolver_evaluate, lotto_evolver_status
        from ..notifiers.telegram_lotto import send_evolution_report
        from ..db import create_task, update_task_status, add_log

        task_id = create_task("lotto", "weekly_evolution_report", {})
        try:
            eval_result = await lotto_evolver_evaluate()
            status = await lotto_evolver_status()
            current_base = status.get("current_base") or [0.2] * 5
            await send_evolution_report(eval_result, current_base)

            winner = eval_result.get("winner") or {}
            update_task_status(task_id, "succeeded", result_data={
                "draw_no": eval_result.get("draw_no"),
                "update_reason": eval_result.get("update_reason"),
                "winner_day_of_week": winner.get("day_of_week"),
                "winner_max_correct": winner.get("max_correct"),
            })
            add_log("lotto", f"weekly_evolution_report 발송: draw={eval_result.get('draw_no')} reason={eval_result.get('update_reason')}", task_id=task_id)
            return {"ok": True, **eval_result}
        except Exception as e:
            update_task_status(task_id, "failed", result_data={"error": str(e)})
            add_log("lotto", f"weekly_evolution_report 예외: {e}", level="error", task_id=task_id)
            return {"ok": False, "message": f"{type(e).__name__}: {e}"}

    async def sync_evolver_activity(self) -> dict:
        """매일 09:30 — lotto-lab evolver 상태 polling → agent_office.db에 task+log 거울. 멱등."""
        from datetime import datetime, timezone, timedelta
        from ..service_proxy import lotto_evolver_status
        from ..db import (
            create_task, update_task_status, add_log,
            get_tasks_by_agent_date_kind,
        )

        KST = timezone(timedelta(hours=9))
        today_kst = datetime.now(KST).date()
        # created_at은 UTC로 저장되므로 idempotency guard는 UTC 날짜 기준
        today_utc_iso = datetime.now(timezone.utc).date().isoformat()
        dow = today_kst.weekday()
        if dow == 6:
            dow = 5

        try:
            status = await lotto_evolver_status()
        except Exception as e:
            add_log("lotto", f"sync_evolver_activity: lotto-lab status fetch 실패: {e}", level="warning")
            return {"ok": False, "reason": "status_fetch_failed", "error": str(e)}

        results = {"created": []}

        today_trial = next((t for t in status.get("trials", []) if t.get("day_of_week") == dow), None)
        if today_trial and today_trial.get("picks"):
            if not get_tasks_by_agent_date_kind("lotto", today_utc_iso, "evolver_apply"):
                tid = create_task("lotto", "evolver_apply", {
                    "date": today_utc_iso,
                    "trial_id": today_trial["id"],
                    "day_of_week": dow,
                    "weight": today_trial["weight"],
                })
                update_task_status(tid, "succeeded", result_data={
                    "n_picks": len(today_trial["picks"]),
                    "meta_scores": [p.get("meta_score") for p in today_trial["picks"]],
                })
                add_log("lotto", f"evolver_apply: 오늘({dow}) W로 {len(today_trial['picks'])}세트 추출", task_id=tid)
                results["created"].append("evolver_apply")

        if today_kst.weekday() == 0 and len(status.get("trials", [])) == 6:
            if not get_tasks_by_agent_date_kind("lotto", today_utc_iso, "evolver_generate"):
                tid = create_task("lotto", "evolver_generate", {"week_start": status.get("week_start")})
                update_task_status(tid, "succeeded", result_data={
                    "trials_count": 6,
                    "candidates_per_source": {"perturb": 4, "dirichlet": 2},
                })
                add_log("lotto", f"evolver_generate: {status.get('week_start')} 주의 6 trials 생성", task_id=tid)
                results["created"].append("evolver_generate")

        return {"ok": True, **results}

    async def _run(self, source: str) -> dict:
        task_id = create_task(self.agent_id, "curate_weekly", {"source": source})
        await self.transition("working", "후보 수집 및 AI 큐레이션 중...", task_id)
        try:
            result = await curate_weekly(source=source)
            update_task_status(task_id, "succeeded", result_data={
                k: v for k, v in result.items() if k != "payload"
            })
            await self.transition("reporting", f"#{result['draw_no']} 브리핑 저장 완료")
            add_log(self.agent_id, f"큐레이션 완료: #{result['draw_no']} conf={result['confidence']}", task_id=task_id)

            # 텔레그램 헤드라인 푸시 (실패해도 큐레이션은 성공으로 마감)
            try:
                from ..notifiers.telegram_lotto import send_curator_briefing
                await send_curator_briefing(result["payload"])
            except Exception as e:
                add_log(self.agent_id, f"텔레그램 알림 실패: {e}", level="warning", task_id=task_id)

            await self.transition("idle", "대기 중")
            return {"ok": True, **{k: v for k, v in result.items() if k != "payload"}}
        except CuratorError as e:
            update_task_status(task_id, "failed", result_data={"error": str(e)})
            add_log(self.agent_id, f"큐레이션 실패: {e}", level="error", task_id=task_id)
            await self.transition("idle", "오류")
            return {"ok": False, "message": str(e)}
        except Exception as e:
            update_task_status(task_id, "failed", result_data={"error": str(e)})
            add_log(self.agent_id, f"큐레이션 예외: {e}", level="error", task_id=task_id)
            await self.transition("idle", "오류")
            return {"ok": False, "message": f"{type(e).__name__}: {e}"}
