"""
Channel Hub — AwoooP 入站事件統一路由 + Progressive Feedback Policy
====================================================================
AwoooP Phase 7: ADR-106（channel_event family）
2026-05-04 ogt + Claude Sonnet 4.6

功能：
  1. Telegram 入站事件鏡像（記錄到 awooop_conversation_event）
  2. 建立 platform run（呼叫 platform_runtime.create_run）
  3. Progressive Feedback Policy：
     - run 進入 WAITING_TOOL 狀態 → 30 秒後若未 complete → 發 interim Telegram 訊息
     - 訊息記錄到 awooop_outbound_message
  4. Shadow Mode：不發任何 Telegram 訊息（只記錄到 outbound_message, status='shadow'）

Progressive Feedback Policy 設計（ADR-106 P2-03）：
  - 用 asyncio.create_task 啟動 30s 計時器
  - 30s 後查詢 run state：若仍在 WAITING_TOOL → 發 interim 訊息
  - interim 訊息：「AI 正在分析中，請稍候...」（不洩漏 run 細節）
  - Final reply 由 shadow_execute() 完成後觸發（Phase 8 實作）

與 legacy telegram_gateway.py 的關係：
  - 完全獨立，不修改 legacy gateway
  - legacy 繼續處理 legacy flow（signal_worker 觸發的 approval/notification）
  - AwoooP run 只走本模組
"""

from __future__ import annotations

import asyncio
import hashlib
import json
from datetime import datetime, timezone
from typing import Any
from uuid import UUID

import structlog
from sqlalchemy import select, text
from sqlalchemy.ext.asyncio import AsyncSession

from src.db.awooop_models import AwoooPRunState
from src.services.audit_sink import _redact_string
from src.services.platform_runtime import create_run

logger = structlog.get_logger(__name__)

# Progressive Feedback Policy：等待超過此秒數才發 interim 訊息
_INTERIM_WAIT_SECONDS = 30


# ─────────────────────────────────────────────────────────────────────────────
# 入站事件記錄
# ─────────────────────────────────────────────────────────────────────────────

async def mirror_inbound_event(
    db: AsyncSession,
    *,
    project_id: str,
    channel_type: str,
    provider_event_id: str,
    platform_subject_id: str | None = None,
    channel_user_id: str | None = None,
    channel_chat_id: str | None = None,
    content_type: str = "text",
    raw_content: str | None = None,
    attachment_sha256: str | None = None,
    provider_ts: datetime | None = None,
    run_id: UUID | None = None,
    is_duplicate: bool = False,
) -> UUID:
    """
    記錄入站 channel event 到 awooop_conversation_event。

    raw_content 只用於計算 hash 和 preview，不入庫明文。
    回傳 event_id。
    """
    content_hash: str | None = None
    content_preview: str | None = None

    if raw_content is not None:
        content_hash = hashlib.sha256(raw_content.encode()).hexdigest()
        # preview：redact 後截取前 256 字元
        redacted = _redact_string(raw_content)
        content_preview = redacted[:256] if len(redacted) > 256 else redacted

    result = await db.execute(
        text("""
            INSERT INTO awooop_conversation_event (
                project_id, channel_type, provider_event_id,
                platform_subject_id, channel_user_id, channel_chat_id,
                run_id, content_type, content_hash, content_preview,
                attachment_sha256, is_duplicate, provider_ts, received_at
            ) VALUES (
                :project_id, :channel_type, :provider_event_id,
                :platform_subject_id, :channel_user_id, :channel_chat_id,
                :run_id, :content_type, :content_hash, :content_preview,
                :attachment_sha256, :is_duplicate, :provider_ts, NOW()
            )
            ON CONFLICT (project_id, channel_type, provider_event_id) DO UPDATE SET
                is_duplicate = TRUE,
                run_id = COALESCE(EXCLUDED.run_id, awooop_conversation_event.run_id)
            RETURNING event_id
        """),
        {
            "project_id": project_id,
            "channel_type": channel_type,
            "provider_event_id": provider_event_id,
            "platform_subject_id": platform_subject_id,
            "channel_user_id": channel_user_id,
            "channel_chat_id": channel_chat_id,
            "run_id": run_id,
            "content_type": content_type,
            "content_hash": content_hash,
            "content_preview": content_preview,
            "attachment_sha256": attachment_sha256,
            "is_duplicate": is_duplicate,
            "provider_ts": provider_ts,
        },
    )
    row = result.fetchone()
    event_id: UUID = row[0]
    logger.info(
        "channel_event_mirrored",
        project_id=project_id,
        channel_type=channel_type,
        event_id=str(event_id),
        is_duplicate=is_duplicate,
    )
    return event_id


# ─────────────────────────────────────────────────────────────────────────────
# 出站訊息記錄
# ─────────────────────────────────────────────────────────────────────────────

async def record_outbound_message(
    db: AsyncSession,
    *,
    project_id: str,
    run_id: UUID,
    channel_type: str,
    channel_chat_id: str,
    message_type: str,           # 'interim' | 'final' | 'error' | 'approval_request'
    content: str | None = None,
    provider_message_id: str | None = None,
    send_status: str = "pending",
    conversation_event_id: UUID | None = None,
    triggered_by_state: str | None = None,
    waiting_since: datetime | None = None,
    is_shadow: bool = True,
) -> UUID:
    """
    記錄出站訊息到 awooop_outbound_message。

    is_shadow=True：status='shadow'（不實際發送，只記錄）
    """
    content_hash: str | None = None
    content_preview: str | None = None
    if content is not None:
        content_hash = hashlib.sha256(content.encode()).hexdigest()
        redacted = _redact_string(content)
        content_preview = redacted[:256]

    actual_status = "shadow" if is_shadow else send_status

    result = await db.execute(
        text("""
            INSERT INTO awooop_outbound_message (
                project_id, run_id, conversation_event_id,
                channel_type, channel_chat_id, message_type,
                content_hash, content_preview, provider_message_id,
                send_status, queued_at,
                triggered_by_state, waiting_since
            ) VALUES (
                :project_id, :run_id, :conversation_event_id,
                :channel_type, :channel_chat_id, :message_type,
                :content_hash, :content_preview, :provider_message_id,
                :send_status, NOW(),
                :triggered_by_state, :waiting_since
            )
            RETURNING message_id
        """),
        {
            "project_id": project_id,
            "run_id": run_id,
            "conversation_event_id": conversation_event_id,
            "channel_type": channel_type,
            "channel_chat_id": channel_chat_id,
            "message_type": message_type,
            "content_hash": content_hash,
            "content_preview": content_preview,
            "provider_message_id": provider_message_id,
            "send_status": actual_status,
            "triggered_by_state": triggered_by_state,
            "waiting_since": waiting_since,
        },
    )
    row = result.fetchone()
    message_id: UUID = row[0]
    logger.info(
        "outbound_message_recorded",
        project_id=project_id,
        run_id=str(run_id),
        message_type=message_type,
        send_status=actual_status,
        message_id=str(message_id),
    )
    return message_id


# ─────────────────────────────────────────────────────────────────────────────
# Progressive Feedback Policy
# ─────────────────────────────────────────────────────────────────────────────

async def schedule_interim_feedback(
    *,
    project_id: str,
    run_id: UUID,
    channel_type: str,
    channel_chat_id: str,
    conversation_event_id: UUID | None = None,
    is_shadow: bool = True,
    wait_seconds: int = _INTERIM_WAIT_SECONDS,
) -> None:
    """
    Progressive Feedback Policy：
    等待 wait_seconds 秒後，若 run 仍在 WAITING_TOOL → 發 interim 訊息。

    Shadow Mode：記錄到 outbound_message(status='shadow')，不實際發 Telegram 訊息。
    """
    asyncio.create_task(
        _interim_feedback_task(
            project_id=project_id,
            run_id=run_id,
            channel_type=channel_type,
            channel_chat_id=channel_chat_id,
            conversation_event_id=conversation_event_id,
            is_shadow=is_shadow,
            wait_seconds=wait_seconds,
        ),
        name=f"interim_feedback_{str(run_id)[:8]}",
    )


async def _interim_feedback_task(
    *,
    project_id: str,
    run_id: UUID,
    channel_type: str,
    channel_chat_id: str,
    conversation_event_id: UUID | None,
    is_shadow: bool,
    wait_seconds: int,
) -> None:
    """等待後查 run state，仍 waiting_tool 才發 interim"""
    await asyncio.sleep(wait_seconds)

    try:
        from src.db.base import get_db_context

        async with get_db_context(project_id) as db:
            result = await db.execute(
                select(AwoooPRunState.state, AwoooPRunState.is_shadow).where(
                    AwoooPRunState.run_id == run_id,
                    AwoooPRunState.project_id == project_id,
                )
            )
            row = result.first()

            if row is None:
                logger.warning(
                    "interim_feedback_run_not_found",
                    run_id=str(run_id),
                )
                return

            state, run_is_shadow = row
            if state != "waiting_tool":
                # run 已推進（complete/failed 等），不需要 interim
                return

            waiting_since = datetime.now(timezone.utc)
            interim_content = "AI 正在分析中，請稍候... ⏳"

            await record_outbound_message(
                db,
                project_id=project_id,
                run_id=run_id,
                channel_type=channel_type,
                channel_chat_id=channel_chat_id,
                message_type="interim",
                content=interim_content,
                send_status="pending",
                conversation_event_id=conversation_event_id,
                triggered_by_state="waiting_tool",
                waiting_since=waiting_since,
                is_shadow=is_shadow or run_is_shadow,
            )

            if not (is_shadow or run_is_shadow):
                # Non-shadow：實際發 Telegram 訊息
                await _send_telegram_interim(
                    channel_chat_id=channel_chat_id,
                    content=interim_content,
                    run_id=run_id,
                )

            logger.info(
                "interim_feedback_sent",
                project_id=project_id,
                run_id=str(run_id),
                is_shadow=is_shadow or run_is_shadow,
            )

    except Exception as exc:
        logger.exception(
            "interim_feedback_task_error",
            run_id=str(run_id),
            error=str(exc),
        )


async def _send_telegram_interim(
    *,
    channel_chat_id: str,
    content: str,
    run_id: UUID,
) -> None:
    """實際發送 Telegram interim 訊息（non-shadow 專用）"""
    try:
        import os

        import httpx

        bot_token = os.environ.get("TELEGRAM_BOT_TOKEN")
        if not bot_token:
            logger.warning("interim_telegram_no_token", run_id=str(run_id))
            return

        async with httpx.AsyncClient(timeout=10) as client:
            await client.post(
                f"https://api.telegram.org/bot{bot_token}/sendMessage",
                json={
                    "chat_id": channel_chat_id,
                    "text": content,
                    "parse_mode": "HTML",
                },
            )
    except Exception as exc:
        logger.warning(
            "interim_telegram_send_failed",
            run_id=str(run_id),
            error=str(exc),
        )


# ─────────────────────────────────────────────────────────────────────────────
# Channel Hub 主入口（Telegram inbound）
# ─────────────────────────────────────────────────────────────────────────────

async def handle_telegram_inbound(
    db: AsyncSession,
    *,
    project_id: str,
    agent_id: str,
    message_id: str,
    user_id: str,
    chat_id: str,
    text: str | None = None,
    is_shadow: bool = True,
) -> dict[str, Any]:
    """
    Telegram 入站訊息的統一處理入口：
    1. mirror_inbound_event（記錄）
    2. create_run（建立 platform run）
    3. schedule_interim_feedback（Progressive Feedback）
    4. 回傳 {event_id, run_id, is_duplicate}
    """
    # Step 1: 嘗試建立 run（有冪等保護）
    run_id, is_duplicate = await create_run(
        project_id=project_id,
        agent_id=agent_id,
        trigger_type="channel_event",
        trigger_ref=f"telegram:{message_id}",
        input_payload={"chat_id": chat_id, "user_id": user_id},
        channel_type="telegram",
        provider_event_id=message_id,
    )

    # Step 2: Mirror event（含 run_id）
    event_id = await mirror_inbound_event(
        db,
        project_id=project_id,
        channel_type="telegram",
        provider_event_id=message_id,
        channel_user_id=user_id,
        channel_chat_id=chat_id,
        content_type="text" if text else "callback_query",
        raw_content=text,
        run_id=run_id,
        is_duplicate=is_duplicate,
    )

    # Step 3: Progressive Feedback（30s 計時器）
    if not is_duplicate:
        await schedule_interim_feedback(
            project_id=project_id,
            run_id=run_id,
            channel_type="telegram",
            channel_chat_id=chat_id,
            conversation_event_id=event_id,
            is_shadow=is_shadow,
        )

    return {
        "event_id": str(event_id),
        "run_id": str(run_id),
        "is_duplicate": is_duplicate,
    }
