"""Feishu WebSocket entry for safe advertising control commands."""

from __future__ import annotations

import logging
import os
import threading
import uuid
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timedelta
from typing import Any
from zoneinfo import ZoneInfo

from agent.tools.builtin.feishu.feishu_client import (
    ChatType,
    FeishuClient,
    FeishuMessageEvent,
)

from command_intent_parser import CommandIntentParser
from operator_commands import (
    ACTION_CANCEL,
    ACTION_CONFIRM,
    ACTION_DAY_PAUSE,
    ACTION_REJECT,
    ACTION_RESUME,
    ACTION_STATUS,
    ACTION_STOP,
    ACTION_TODAY_SPEND,
    normalize_text,
    parse_command,
)
from operator_control import (
    cancel_command,
    execute_confirmed_command,
    pause_status_summary,
    preview_write_command,
)
from realtime_config import RealtimeControlConfig
from storage import (
    close_operator_draft,
    list_pending_operator_commands,
    load_active_operator_draft,
    load_operator_command_by_source,
    save_operator_draft,
)
from today_spend_query import query_today_spend


SHANGHAI = ZoneInfo("Asia/Shanghai")
logger = logging.getLogger("tencent_realtime_control.feishu_commands")

_ACTION_LABELS = {
    ACTION_DAY_PAUSE: "仅暂停今天",
    ACTION_STOP: "持续停止",
    ACTION_RESUME: "恢复投放",
}


def _split_ids(raw: str) -> set[str]:
    normalized = str(raw or "").replace("，", ",").replace(" ", ",")
    return {value.strip() for value in normalized.split(",") if value.strip()}


class FeishuCommandService:
    def __init__(self, *, apply: bool) -> None:
        app_id = os.getenv("FEISHU_APP_ID", "").strip()
        app_secret = os.getenv("FEISHU_APP_SECRET", "").strip()
        if not app_id or not app_secret:
            raise RuntimeError("FEISHU_APP_ID and FEISHU_APP_SECRET are required")
        self.allowed_chat_id = (
            os.getenv("RTC_COMMAND_CHAT_ID", "").strip()
            or os.getenv("FEISHU_AD_PROJECT_CHAT_ID", "").strip()
        )
        self.allowed_open_ids = _split_ids(
            os.getenv("RTC_COMMAND_ALLOWED_OPEN_IDS", "")
            or os.getenv("FEISHU_OPERATOR_OPEN_ID", "")
        )
        if not self.allowed_chat_id:
            raise RuntimeError("RTC_COMMAND_CHAT_ID/FEISHU_AD_PROJECT_CHAT_ID is required")
        if not self.allowed_open_ids:
            raise RuntimeError(
                "RTC_COMMAND_ALLOWED_OPEN_IDS/FEISHU_OPERATOR_OPEN_ID is required"
            )
        self.apply = apply
        self.config = RealtimeControlConfig.from_env()
        self.delivery_start_hour = self.config.next_delivery_hour
        self.confirmation_ttl_minutes = int(
            os.getenv("RTC_COMMAND_CONFIRM_TTL_MINUTES", "10")
        )
        self.draft_ttl_minutes = int(os.getenv("RTC_COMMAND_DRAFT_TTL_MINUTES", "5"))
        if self.confirmation_ttl_minutes < 1 or self.draft_ttl_minutes < 1:
            raise ValueError("command TTL values must be positive")
        self.intent_parser = CommandIntentParser()
        self.client = FeishuClient(app_id=app_id, app_secret=app_secret)
        self.executor = ThreadPoolExecutor(
            max_workers=int(os.getenv("RTC_COMMAND_WORKERS", "2")),
            thread_name_prefix="feishu-ad-command",
        )
        self._conversation_locks: dict[tuple[str, str], threading.Lock] = {}
        self._conversation_locks_guard = threading.Lock()

    def start(self) -> Any:
        return self.client.start_websocket(
            on_message=self._enqueue_message,
            blocking=False,
        )

    def _enqueue_message(self, event: FeishuMessageEvent) -> None:
        if not self._authorized(event):
            return
        self.executor.submit(self._handle_serialized, event)

    def _handle_serialized(self, event: FeishuMessageEvent) -> None:
        key = (event.chat_id, event.sender_open_id)
        with self._conversation_locks_guard:
            lock = self._conversation_locks.setdefault(key, threading.Lock())
        with lock:
            self.handle_message(event)

    def _reply(self, event: FeishuMessageEvent, text: str) -> None:
        self.client.send_message(
            to=event.chat_id,
            text=text,
            reply_to_message_id=event.message_id,
        )

    def _authorized(self, event: FeishuMessageEvent) -> bool:
        return (
            event.content_type in {"text", "post"}
            and event.chat_type == ChatType.GROUP
            and event.chat_id == self.allowed_chat_id
            and event.sender_open_id in self.allowed_open_ids
            and event.mentioned_bot
        )

    def _reply_pending_commands(self, event: FeishuMessageEvent, now: datetime) -> None:
        commands = list_pending_operator_commands(
            event.chat_id, event.sender_open_id, now
        )
        if not commands:
            self._reply(event, "当前没有待确认命令。")
            return
        lines = ["请指定要确认的命令ID："]
        for command in commands:
            lines.append(
                f"- {command['command_id']} / {_ACTION_LABELS.get(command['action'], command['action'])} "
                f"/ {command['preview_account_count']}个账户 "
                f"/ {command['preview_ad_count']}条广告 "
                f"/ 今日消耗{int(command.get('preview_cost_fen') or 0) / 100:.2f}元"
            )
        self._reply(event, "\n".join(lines))

    def _handle_command_id_action(
        self,
        event: FeishuMessageEvent,
        now: datetime,
    ) -> bool:
        text = normalize_text(event.content)
        if text == "确认":
            self._reply_pending_commands(event, now)
            return True
        if text in {"取消", "取消对话", "取消草稿"}:
            close_operator_draft(event.chat_id, event.sender_open_id, "CANCELLED")
            self._reply(event, "当前未完成的对话已取消。")
            return True
        if not text.startswith(("确认", "取消", "拒绝")):
            return False
        parsed = parse_command(text)
        if parsed is None:
            return False
        if parsed.action == ACTION_CANCEL:
            if (parsed.command_id or "").startswith("roi_"):
                raise ValueError("ROI批次请在审批表黄色列逐行处理")
            command = cancel_command(parsed.command_id or "", event.sender_open_id, now)
            message = (
                f"命令 {command['command_id']} 已取消"
                if command["status"] == "CANCELLED"
                else f"命令 {command['command_id']} 未取消，当前状态：{command['status']}"
            )
            self._reply(event, message)
            return True
        if parsed.action == ACTION_REJECT:
            self._reply(event, "ROI批次请在审批表黄色列逐行批准或拒绝。")
            return True
        if parsed.action != ACTION_CONFIRM:
            return False
        if (parsed.command_id or "").startswith("roi_"):
            self._reply(event, "ROI批次请在审批表黄色列逐行批准或拒绝。")
            return True
        if not self.apply:
            self._reply(event, "当前服务为 dry-run，禁止执行腾讯写操作。")
            return True
        command = execute_confirmed_command(
            parsed.command_id or "",
            sender_open_id=event.sender_open_id,
            now=now,
            start_hour=self.delivery_start_hour,
            lock_name=self.config.lock_name,
        )
        account_ids = ",".join(
            str(value) for value in command.get("target_account_ids") or []
        )
        self._reply(
            event,
            f"命令 {command['command_id']} 执行完成\n"
            f"- 状态：{command['status']}\n"
            f"- 账户：{account_ids or '无'}\n"
            f"- 预览时今日消耗："
            f"{int(command.get('preview_cost_fen') or 0) / 100:.2f}元\n"
            f"- 成功：{command.get('successes', 0)} 条\n"
            f"- 跳过：{command.get('skipped', 0)} 条\n"
            f"- 失败：{command.get('failures', 0)} 条",
        )
        return True

    def _save_incomplete_intent(
        self,
        event: FeishuMessageEvent,
        intent: Any,
        draft: dict[str, Any] | None,
        now: datetime,
    ) -> None:
        raw_text = "\n".join(
            value for value in ((draft or {}).get("raw_text"), event.content) if value
        )
        source_ids = list((draft or {}).get("source_message_ids") or [])
        source_ids.append(event.message_id)
        save_operator_draft({
            "draft_id": f"draft_{uuid.uuid4().hex}",
            "chat_id": event.chat_id,
            "sender_open_id": event.sender_open_id,
            "raw_text": raw_text,
            "action": intent.action,
            "scope_type": intent.scope_type,
            "account_ids": list(intent.account_ids),
            "missing_fields": list(intent.missing_fields or ("scope",)),
            "source_message_ids": source_ids,
            "expires_at": now + timedelta(minutes=self.draft_ttl_minutes),
        })
        if "clarification" in intent.missing_fields:
            question = "我没有准确理解该操作。请明确发送：暂停今天、持续停止、恢复投放或查询暂停状态。"
        else:
            if intent.action == ACTION_TODAY_SPEND:
                scope_hint = "自动化账户、全部账户，或一个/多个账户ID"
            else:
                scope_hint = "全部纳管账户，或一个/多个账户ID"
            question = (
                f"请指定操作范围：{scope_hint}。\n"
                f"该对话将在 {self.draft_ttl_minutes} 分钟后过期。"
            )
        self._reply(event, question)

    def _reply_preview(self, event: FeishuMessageEvent, command: dict[str, Any]) -> None:
        lines = [
            f"操作预览：{_ACTION_LABELS[command['action']]}",
            f"- 数据时间：{command['previewed_at'].strftime('%Y-%m-%d %H:%M:%S')}",
        ]
        if command["action"] == ACTION_DAY_PAUSE:
            lines.append(
                "- 次日自动投放："
                f"{command['resume_at'].strftime('%Y-%m-%d %H:%M')}（腾讯投放时段）"
            )
        elif command["action"] == ACTION_STOP:
            lines.append("- 恢复方式：后续发送恢复命令")
        lines.append("- 影响账户：")
        for row in command.get("account_summaries") or []:
            lines.append(
                f"  - {row['account_id']}：{row['ad_count']}条广告，"
                f"今日消耗{int(row['cost_fen']) / 100:.2f}元"
            )
        lines.extend([
            f"- 汇总：{command['preview_account_count']}个账户，"
            f"{command['preview_ad_count']}条广告，今日消耗"
            f"{int(command.get('preview_cost_fen') or 0) / 100:.2f}元",
            f"- 命令ID：{command['command_id']}",
            f"请在 {self.confirmation_ttl_minutes} 分钟内回复：确认 {command['command_id']}",
            f"取消命令请回复：取消 {command['command_id']}",
        ])
        self._reply(event, "\n".join(lines))

    def handle_message(self, event: FeishuMessageEvent) -> None:
        if not self._authorized(event):
            return
        now = datetime.now(SHANGHAI)
        try:
            existing = load_operator_command_by_source(event.message_id)
            if existing:
                self._reply(
                    event,
                    f"该消息已处理：{existing['command_id']}，"
                    f"当前状态 {existing['status']}。",
                )
                return
            if self._handle_command_id_action(event, now):
                return
            draft = load_active_operator_draft(
                event.chat_id, event.sender_open_id, now
            )
            try:
                intent = self.intent_parser.understand(event.content, draft=draft)
            except Exception as exc:
                logger.exception("Natural-language command parsing failed")
                self._reply(
                    event,
                    "自然语言理解暂时不可用，标准命令仍可使用：\n"
                    "- 暂停全部\n- 暂停 账户ID\n- 停止 账户ID\n- 恢复 账户ID",
                )
                return
            if intent is None:
                self._reply(
                    event,
                    "未识别为投放控制命令。可使用：暂停今天、持续停止、恢复投放、查询暂停状态。",
                )
                return
            if (
                draft
                and intent.action
                and intent.action != draft.get("action")
            ):
                close_operator_draft(
                    event.chat_id,
                    event.sender_open_id,
                    "SUPERSEDED",
                )
                draft = None
            if not intent.complete:
                self._save_incomplete_intent(event, intent, draft, now)
                return
            if draft:
                close_operator_draft(event.chat_id, event.sender_open_id, "COMPLETED")
            if intent.action == ACTION_STATUS:
                summary = pause_status_summary(now)
                accounts = ",".join(str(value) for value in summary["accounts"]) or "无"
                self._reply(
                    event,
                    "当前运营暂停状态\n"
                    f"- 暂停广告：{summary['total']} 条\n"
                    f"- 仅暂停今天：{summary['until_next_delivery']} 条\n"
                    f"- 持续停止：{summary['until_manual']} 条\n"
                    f"- 涉及账户：{accounts}",
                )
                return
            if intent.action == ACTION_TODAY_SPEND:
                summary = query_today_spend(
                    intent.to_parsed_command(),
                    now=now,
                )
                amount_label = (
                    "今日总消耗"
                    if summary["complete"]
                    else "成功账户消耗合计"
                )
                status = "完整" if summary["complete"] else "部分数据"
                self._reply(
                    event,
                    f"今日投放总览（{summary['scope_label']}）\n"
                    f"- 数据日期：{summary['data_date']}\n"
                    f"- 查询时间：{summary['queried_at'].strftime('%Y-%m-%d %H:%M:%S')}\n"
                    f"- 查询状态：{status}\n"
                    f"- 账户数量：{summary['account_count']} 个\n"
                    f"- 有消耗账户：{summary['spending_account_count']} 个\n"
                    f"- {amount_label}：{summary['cost_fen'] / 100:.2f} 元\n"
                    f"- 曝光：{summary['impressions']:,}\n"
                    f"- 点击：{summary['clicks']:,}\n"
                    f"- 转化：{summary['conversions']:,}\n"
                    f"- 查询失败账户：{summary['failed_account_count']} 个",
                )
                return
            combined_text = "\n".join(
                value for value in ((draft or {}).get("raw_text"), event.content) if value
            )
            command = preview_write_command(
                intent.to_parsed_command(),
                now=now,
                source_message_id=event.message_id,
                chat_id=event.chat_id,
                sender_open_id=event.sender_open_id,
                sender_name=event.sender_name,
                confirmation_ttl_minutes=self.confirmation_ttl_minutes,
                start_hour=self.delivery_start_hour,
                raw_text=combined_text,
                parse_source=intent.parse_source,
                preview_lock_name=f"{self.config.lock_name}:operator-preview",
                intent={
                    "action": intent.action,
                    "scope_type": intent.scope_type,
                    "account_ids": list(intent.account_ids),
                    "confidence": intent.confidence,
                },
            )
            self._reply_preview(event, command)
        except Exception as exc:
            logger.exception("Feishu command failed")
            self._reply(event, f"命令处理失败：{exc}")
