"""텔레그램 Webhook 이벤트 처리."""
from typing import Optional

from .. import service_proxy
from ..db import get_telegram_callback, mark_telegram_responded
from .client import _enabled, api_call


async def handle_webhook(data: dict, agent_dispatcher=None) -> Optional[dict]:
    """텔레그램에서 들어오는 이벤트 처리.

    - callback_query(인라인 버튼)는 항상 처리 → 승인/거절 dict 반환
    - message(텍스트 슬래시 명령)는 `agent_dispatcher`가 주입된 경우에만 처리

    agent_dispatcher: async (agent_id, command, params) -> dict
      - agent_id == "__global__", command == "status" 특수 케이스는
        {agent_id: {state, detail}} dict를 반환해야 함.
    """
    callback_query = data.get("callback_query")
    if callback_query:
        return await _handle_callback(callback_query)

    message = data.get("message")
    if message:
        chat = message.get("chat", {})
        print(f"[TG-WEBHOOK] chat.id={chat.get('id')} type={chat.get('type')} text={message.get('text')!r}", flush=True)
    if message and message.get("text"):
        if await handle_watch_command(message):
            return None
    if message and message.get("text") and agent_dispatcher is not None:
        return await _handle_message(message, agent_dispatcher)

    return None


async def handle_watch_command(message: dict) -> bool:
    """/watch /unwatch /watchlist 명령을 처리해 stock watchlist API로 프록시.

    처리했으면(응답 전송 포함) True, 매칭되지 않는 텍스트면 False."""
    text = (message.get("text") or "").strip()
    chat_id = message.get("chat", {}).get("id")
    parts = text.split()
    cmd = parts[0].lower() if parts else ""

    if cmd == "/watch" and len(parts) >= 2:
        await service_proxy.watchlist_add(parts[1])
        reply = f"관심종목 추가: {parts[1]}"
    elif cmd == "/unwatch" and len(parts) >= 2:
        await service_proxy.watchlist_remove(parts[1])
        reply = f"관심종목 삭제: {parts[1]}"
    elif cmd == "/watchlist":
        res = await service_proxy.watchlist_list()
        items = res.get("watchlist", [])
        reply = "관심종목:\n" + (
            "\n".join(f"- {w.get('name') or ''} ({w['ticker']})" for w in items) or "(없음)"
        )
    else:
        return False

    await api_call("sendMessage", {"chat_id": chat_id, "text": reply})
    return True


async def _handle_callback(callback_query: dict) -> Optional[dict]:
    """승인/거절 및 realestate 북마크 콜백 처리."""
    callback_id = callback_query.get("data", "")

    # realestate 북마크 토글 콜백 — DB 조회 없이 직접 처리
    if callback_id.startswith("realestate_bookmark_"):
        return await _handle_realestate_bookmark(callback_query, callback_id)

    if callback_id.startswith("render_"):
        return await _handle_insta_render(callback_query, callback_id)

    if callback_id.startswith("issue_"):
        return await _handle_insta_issue(callback_query, callback_id)

    if callback_id.startswith("ytpub_retry_"):
        return await _handle_ytpub_retry(callback_query, callback_id)

    cb = get_telegram_callback(callback_id)
    if not cb:
        return None

    action = callback_id.split("_")[0]
    mark_telegram_responded(callback_id, action)

    feedback_text = {
        "approve": "승인됨 ✅",
        "reject": "거절됨 ❌",
    }.get(action, f"처리됨: {action}")

    await api_call(
        "answerCallbackQuery",
        {
            "callback_query_id": callback_query["id"],
            "text": feedback_text,
        },
    )

    return {
        "task_id": cb["task_id"],
        "agent_id": cb["agent_id"],
        "action": action,
        "approved": action == "approve",
    }


async def _handle_realestate_bookmark(callback_query: dict, callback_id: str) -> dict:
    """realestate_bookmark_{announcement_id} 콜백 처리."""
    from .. import service_proxy
    from .messaging import send_raw

    # answerCallbackQuery 먼저 — 텔레그램 로딩 스피너 해제
    await api_call(
        "answerCallbackQuery",
        {"callback_query_id": callback_query["id"], "text": "처리 중..."},
    )

    try:
        ann_id = int(callback_id.removeprefix("realestate_bookmark_"))
    except ValueError:
        await send_raw("⚠️ 잘못된 북마크 콜백 데이터")
        return {"ok": False, "error": "invalid_callback_data"}

    try:
        result = await service_proxy.realestate_bookmark_toggle(ann_id)
        is_on = result.get("is_bookmarked")
        if is_on == 1:
            await send_raw(f"🔖 북마크 추가 완료 (#{ann_id})")
        elif is_on == 0:
            await send_raw(f"🔖 북마크 해제 완료 (#{ann_id})")
        else:
            await send_raw(f"🔖 북마크 토글 완료 (#{ann_id})")
        return {"ok": True, "announcement_id": ann_id}
    except Exception as e:
        await send_raw(f"⚠️ 북마크 처리 실패: {e}")
        return {"ok": False, "error": str(e)}


async def _handle_insta_render(callback_query: dict, callback_id: str) -> dict:
    """render_{keyword_id} 콜백 → InstaAgent.on_callback('render', ...).

    텔레그램 인라인 버튼이 보낸 callback_data가 `render_<keyword_id>` 형식.
    InstaAgent._push_keyword_candidates가 callback_data를 그대로 박아 보내며,
    별도 DB lookup 없이 keyword_id를 파싱해 dispatch한다."""
    from .messaging import send_raw
    from ..agents import AGENT_REGISTRY

    await api_call(
        "answerCallbackQuery",
        {"callback_query_id": callback_query["id"], "text": "카드 생성 시작"},
    )

    try:
        keyword_id = int(callback_id.removeprefix("render_"))
    except ValueError:
        await send_raw("⚠️ 잘못된 render 콜백 데이터")
        return {"ok": False, "error": "invalid_callback_data"}

    agent = AGENT_REGISTRY.get("insta")
    if not agent:
        await send_raw("⚠️ insta agent 미등록")
        return {"ok": False, "error": "agent_missing"}

    try:
        return await agent.on_callback("render", {"keyword_id": keyword_id})
    except Exception as e:
        await send_raw(f"⚠️ 카드 생성 실패: {e}")
        return {"ok": False, "error": str(e)}


async def _handle_insta_issue(callback_query: dict, callback_id: str) -> dict:
    """issue_{approve|reject|regen}_{slate_id} 콜백 → InstaAgent.on_callback.

    callback_data 예시: issue_approve_8, issue_reject_8, issue_regen_8
    InstaAgent.on_callback("issue_approve" | "issue_reject" | "issue_regen", {"slate_id": <int>}) 로 dispatch.
    """
    from .messaging import send_raw
    from ..agents import AGENT_REGISTRY

    await api_call(
        "answerCallbackQuery",
        {"callback_query_id": callback_query["id"], "text": "처리 중..."},
    )

    try:
        rest = callback_id.removeprefix("issue_")   # 예: "approve_8"
        verb, sid = rest.rsplit("_", 1)             # ("approve", "8")
        slate_id = int(sid)
    except (ValueError, AttributeError):
        await send_raw("⚠️ 잘못된 issue 콜백 데이터")
        return {"ok": False, "error": "invalid_callback_data"}

    agent = AGENT_REGISTRY.get("insta")
    if not agent:
        await send_raw("⚠️ insta agent 미등록")
        return {"ok": False, "error": "agent_missing"}

    try:
        return await agent.on_callback(f"issue_{verb}", {"slate_id": slate_id})
    except Exception as e:
        await send_raw(f"⚠️ issue 콜백 처리 실패: {e}")
        return {"ok": False, "error": str(e)}


async def _handle_ytpub_retry(callback_query: dict, callback_id: str) -> dict:
    """ytpub_retry_{pipeline_id} 콜백 → music-lab pipeline retry 프록시."""
    from .. import service_proxy
    from .messaging import send_raw

    await api_call(
        "answerCallbackQuery",
        {"callback_query_id": callback_query["id"], "text": "재시도 요청 중..."},
    )

    try:
        pid = int(callback_id.removeprefix("ytpub_retry_"))
    except (ValueError, AttributeError):
        return {"ok": False, "error": "invalid_callback_data"}

    res = await service_proxy.pipeline_retry(pid)
    sc = res.get("status_code")
    if sc in (200, 202):
        await send_raw(text=f"🔄 파이프라인 #{pid} 재개: {res.get('retrying_step', '?')}")
    else:
        await send_raw(text=f"⚠️ 재개 불가 (#{pid}): {res.get('detail', sc)}")
    return {"ok": True}


async def _handle_message(message: dict, agent_dispatcher) -> Optional[dict]:
    """슬래시 명령 메시지 처리."""
    from .router import parse_command, resolve_agent_command, HELP_TEXT
    from .messaging import send_raw, send_agent_message
    from .agent_registry import AGENT_META
    from .conversational import maybe_route_to_pipeline

    # 파이프라인 메시지에 대한 reply라면 youtube_publisher 로 라우팅
    if await maybe_route_to_pipeline(message):
        return {"handled": "pipeline_reply"}

    text = message.get("text", "")
    parsed = parse_command(text)
    if not parsed:
        # 슬래시 명령이 아니면 자연어 대화로 라우팅
        chat_id = str(message.get("chat", {}).get("id", ""))
        if not chat_id:
            return None
        from .conversational import respond_to_message
        reply = await respond_to_message(chat_id, text)
        if reply:
            import html as _html
            await send_raw(_html.escape(reply), chat_id=chat_id)
            return {"handled": "chat"}
        return None

    agent_id, command, args = parsed

    # 전역 명령
    if agent_id is None:
        if command == "help":
            await send_raw(HELP_TEXT)
            return {"handled": "help"}

        if command == "agents":
            lines = ["<b>📋 등록된 에이전트</b>", ""]
            for aid, meta in AGENT_META.items():
                lines.append(
                    f"{meta['emoji']} <b>{meta['display_name']}</b> <code>/{aid}</code>"
                )
            await send_raw("\n".join(lines))
            return {"handled": "agents"}

        if command == "status":
            try:
                result = await agent_dispatcher("__global__", "status", {})
                body_lines = []
                if isinstance(result, dict):
                    for aid, info in result.items():
                        meta = AGENT_META.get(
                            aid, {"emoji": "🤖", "display_name": aid}
                        )
                        state = info.get("state", "unknown") if isinstance(info, dict) else "unknown"
                        body_lines.append(
                            f"{meta['emoji']} <b>{meta['display_name']}</b>: <code>{state}</code>"
                        )
                        detail = info.get("detail") if isinstance(info, dict) else None
                        if detail:
                            body_lines.append(f"  └ {detail}")
                await send_raw("<b>📊 전체 상태</b>\n\n" + "\n".join(body_lines))
            except Exception as e:
                await send_raw(f"⚠️ 상태 조회 실패: {e}")
            return {"handled": "status"}

        return None

    # 에이전트 명령
    if agent_id not in AGENT_META:
        await send_raw(
            f"⚠️ 알 수 없는 에이전트: <code>{agent_id}</code>\n/help 로 사용 가능한 명령 확인"
        )
        return {"handled": "unknown_agent"}

    resolved = resolve_agent_command(agent_id, command, args)
    if resolved is None:
        await send_raw(
            f"⚠️ <code>{agent_id}</code>에서 <code>{command}</code> 명령은 지원하지 않습니다."
        )
        return {"handled": "unknown_command"}

    internal_cmd, params = resolved

    try:
        result = await agent_dispatcher(agent_id, internal_cmd, params)
        ok = result.get("ok", False) if isinstance(result, dict) else False
        msg = result.get("message", "") if isinstance(result, dict) else str(result)

        await send_agent_message(
            agent_id=agent_id,
            kind="info" if ok else "error",
            title=f"{internal_cmd} 실행 결과",
            body=msg or str(result),
        )
    except Exception as e:
        await send_raw(f"⚠️ 명령 실행 실패: {e}")

    return {"handled": "command", "agent_id": agent_id, "command": internal_cmd}


async def setup_webhook() -> dict:
    from ..config import TELEGRAM_WEBHOOK_URL

    if not _enabled() or not TELEGRAM_WEBHOOK_URL:
        return {"ok": False, "description": "Webhook URL not configured"}
    return await api_call("setWebhook", {"url": TELEGRAM_WEBHOOK_URL})
