"""
Provider Proxy Adapter — EwoooC AwoooP Envelope 注入
=====================================================
AwoooP Phase 6: ADR-115 D3
2026-05-04 ogt + Claude Sonnet 4.6

功能：
  EwoooC（或任何外部 tenant）的請求在進入 AwoooP 前，
  必須注入完整的 platform envelope，確保：
  - project_id 正確（budget/audit/RLS 有效）
  - agent_id 存在（Gate 2 通過）
  - trace_id / run_id 有 W3C traceparent format
  - platform_subject_id 已建立（channel user 身份映射）

使用方式：
    from src.services.provider_proxy import ProviderProxy

    proxy = ProviderProxy(project_id="ewoooc", db=db)
    envelope = await proxy.build_envelope(
        agent_id="openclaw-biz",
        channel_type="telegram",
        channel_user_id="123456789",
        channel_chat_id="123456789",
    )
    # envelope 可直接作為 GatewayContext 的初始化參數

設計原則（ADR-115 D3）：
  - Proxy 只做 envelope 注入（<1ms），不做額外複雜 IO
  - platform_subject upsert 是唯一 DB write（auto-provisioning）
  - run_id 由 platform_runtime.create_run() 分配，Proxy 不自行生成
  - 每個 tenant 有獨立的 budget partition 和 RLS 隔離
"""

from __future__ import annotations

import hashlib
import os
import re
import struct
import time
import uuid
from dataclasses import dataclass, field
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 AwoooPPlatformSubject, AwoooPProject

logger = structlog.get_logger(__name__)

# ─────────────────────────────────────────────────────────────────────────────
# Platform Envelope
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class PlatformEnvelope:
    """
    AwoooP Platform Envelope — 每個 EwoooC 請求注入的 metadata。

    下游（Gateway / Budget / Audit）都依賴這個 envelope。
    """
    project_id: str
    agent_id: str
    trace_id: str              # W3C traceparent
    platform_subject_id: str   # "{project_id}:{channel_type}:{channel_user_id}"
    channel_type: str
    channel_user_id: str
    channel_chat_id: str | None = None
    run_id: UUID | None = None             # 由 create_run() 填入
    policy_revision_id: str | None = None  # active policy contract revision
    tags: dict[str, Any] = field(default_factory=dict)

    def as_dict(self) -> dict[str, Any]:
        return {
            "project_id": self.project_id,
            "agent_id": self.agent_id,
            "trace_id": self.trace_id,
            "platform_subject_id": self.platform_subject_id,
            "channel_type": self.channel_type,
            "channel_user_id": self.channel_user_id,
            "channel_chat_id": self.channel_chat_id,
            "run_id": str(self.run_id) if self.run_id else None,
            "policy_revision_id": self.policy_revision_id,
        }


# ─────────────────────────────────────────────────────────────────────────────
# W3C traceparent 生成
# ─────────────────────────────────────────────────────────────────────────────

def _new_trace_id() -> str:
    """生成 W3C traceparent 格式 trace_id。格式：00-{32hex}-{16hex}-01"""
    trace_id = uuid.uuid4().hex  # 32 hex chars = 128 bits
    span_id = uuid.uuid4().hex[:16]  # 16 hex chars = 64 bits
    return f"00-{trace_id}-{span_id}-01"


# ─────────────────────────────────────────────────────────────────────────────
# platform_subject_id 格式
# ─────────────────────────────────────────────────────────────────────────────

def build_platform_subject_id(project_id: str, channel_type: str, channel_user_id: str) -> str:
    """
    格式：{project_id}:{channel_type}:{channel_user_id}
    例：ewoooc:telegram:123456789
    """
    return f"{project_id}:{channel_type}:{channel_user_id}"


# ─────────────────────────────────────────────────────────────────────────────
# ProviderProxy
# ─────────────────────────────────────────────────────────────────────────────

class ProviderProxy:
    """
    AwoooP Provider Proxy Adapter（ADR-115 D3）。

    職責：
    1. 驗證 project 存在且不是 legacy mode
    2. upsert platform_subject（auto-provisioning）
    3. 生成 trace_id（W3C traceparent）
    4. 返回 PlatformEnvelope 供下游使用
    """

    def __init__(self, project_id: str, db: AsyncSession) -> None:
        self.project_id = project_id
        self._db = db

    async def build_envelope(
        self,
        *,
        agent_id: str,
        channel_type: str,
        channel_user_id: str,
        channel_chat_id: str | None = None,
        display_name: str | None = None,
        extra_tags: dict[str, Any] | None = None,
    ) -> PlatformEnvelope:
        """
        建立 PlatformEnvelope：
        1. 驗證 project_id（不是 legacy mode）
        2. upsert platform_subject（auto-provisioning）
        3. 生成 trace_id
        4. 返回 envelope
        """
        await self._validate_project()
        await self._upsert_platform_subject(
            channel_type=channel_type,
            channel_user_id=channel_user_id,
            channel_chat_id=channel_chat_id,
            display_name=display_name,
        )

        platform_subject_id = build_platform_subject_id(
            self.project_id, channel_type, channel_user_id
        )
        trace_id = _new_trace_id()

        logger.info(
            "provider_proxy_envelope_built",
            project_id=self.project_id,
            agent_id=agent_id,
            channel_type=channel_type,
            platform_subject_id=platform_subject_id,
            trace_id=trace_id[:32] + "...",  # 只 log 前 32 字元
        )

        return PlatformEnvelope(
            project_id=self.project_id,
            agent_id=agent_id,
            trace_id=trace_id,
            platform_subject_id=platform_subject_id,
            channel_type=channel_type,
            channel_user_id=channel_user_id,
            channel_chat_id=channel_chat_id,
            tags=extra_tags or {},
        )

    async def _validate_project(self) -> None:
        """project 必須存在且不是 legacy_awoooi_default mode"""
        result = await self._db.execute(
            select(AwoooPProject).where(
                AwoooPProject.project_id == self.project_id,
                AwoooPProject.migration_mode != "legacy_awoooi_default",
            )
        )
        project = result.scalar_one_or_none()
        if project is None:
            raise ValueError(
                f"project '{self.project_id}' 不存在或 migration_mode=legacy_awoooi_default"
                "（EwoooC 接入需要至少 migration_mode='shadow'）"
            )

    async def _upsert_platform_subject(
        self,
        *,
        channel_type: str,
        channel_user_id: str,
        channel_chat_id: str | None,
        display_name: str | None,
    ) -> None:
        """
        Auto-provisioning：第一次看到這個 channel user 就建立 platform_subject。
        後續請求更新 last_seen_at。
        """
        platform_subject_id = build_platform_subject_id(
            self.project_id, channel_type, channel_user_id
        )
        now = datetime.now(timezone.utc)

        await self._db.execute(
            text("""
                INSERT INTO awooop_platform_subjects (
                    project_id, channel_type, channel_user_id, channel_chat_id,
                    platform_subject_id, display_name, roles,
                    first_seen_at, last_seen_at
                ) VALUES (
                    :project_id, :channel_type, :channel_user_id, :channel_chat_id,
                    :platform_subject_id, :display_name, '["viewer"]'::jsonb,
                    :now, :now
                )
                ON CONFLICT (project_id, channel_type, channel_user_id) DO UPDATE SET
                    last_seen_at = :now,
                    channel_chat_id = COALESCE(EXCLUDED.channel_chat_id, awooop_platform_subjects.channel_chat_id),
                    display_name = COALESCE(EXCLUDED.display_name, awooop_platform_subjects.display_name)
            """),
            {
                "project_id": self.project_id,
                "channel_type": channel_type,
                "channel_user_id": channel_user_id,
                "channel_chat_id": channel_chat_id,
                "platform_subject_id": platform_subject_id,
                "display_name": display_name,
                "now": now,
            },
        )
