"""
EmbeddingService: Embedding 抽象基类及实现
- EmbeddingService: ABC
- OpenAIEmbedder: 调用 OpenAI text-embedding-3-small（httpx async）
- MockEmbedder: 返回随机向量，用于测试
"""
import logging
from abc import ABC, abstractmethod
from typing import Optional

import httpx

logger = logging.getLogger(__name__)


class EmbeddingService(ABC):
    """Embedding 抽象基类"""

    @abstractmethod
    async def embed(self, text: str) -> list[float]:
        """单文本 embedding"""

    @abstractmethod
    async def embed_batch(self, texts: list[str]) -> list[list[float]]:
        """批量 embedding"""

    @property
    @abstractmethod
    def dimension(self) -> int:
        """向量维度"""


class OpenAIEmbedder(EmbeddingService):
    """OpenAI Embedding 实现（text-embedding-3-small, dim=1536）"""

    def __init__(
        self,
        model: str = "text-embedding-3-small",
        api_key: Optional[str] = None,
        base_url: str = "https://api.openai.com/v1/embeddings",
        timeout: float = 30.0,
    ):
        self.model = model
        self.base_url = base_url
        self.timeout = timeout
        self._dimension = 1536

        # 获取 API Key：优先参数传入，其次从 settings 读取
        if api_key:
            self.api_key = api_key
        else:
            try:
                from app.config import settings
                self.api_key = getattr(settings, "OPENAI_API_KEY", "")
            except Exception:
                self.api_key = ""

    @property
    def dimension(self) -> int:
        return self._dimension

    async def embed(self, text: str) -> list[float]:
        """调用 OpenAI API 获取单条 embedding"""
        results = await self.embed_batch([text])
        return results[0]

    async def embed_batch(
        self, texts: list[str], batch_size: int = 100
    ) -> list[list[float]]:
        """批量处理，每批最多 batch_size 条"""
        if not texts:
            return []

        all_embeddings: list[list[float]] = []

        async with httpx.AsyncClient(timeout=self.timeout) as client:
            for i in range(0, len(texts), batch_size):
                batch = texts[i : i + batch_size]
                embeddings = await self._call_api(client, batch)
                all_embeddings.extend(embeddings)

        return all_embeddings

    async def _call_api(
        self, client: httpx.AsyncClient, texts: list[str]
    ) -> list[list[float]]:
        """发起单次 API 请求"""
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }
        payload = {
            "model": self.model,
            "input": texts,
        }

        response = await client.post(self.base_url, json=payload, headers=headers)
        response.raise_for_status()

        data = response.json()
        # OpenAI 返回格式：{"data": [{"index": i, "embedding": [...]}]}
        items = sorted(data["data"], key=lambda x: x["index"])
        return [item["embedding"] for item in items]


class MockEmbedder(EmbeddingService):
    """Mock 实现，返回随机向量，用于测试/开发环境"""

    def __init__(self, dimension: int = 1536):
        self._dimension = dimension

    @property
    def dimension(self) -> int:
        return self._dimension

    async def embed(self, text: str) -> list[float]:
        import random
        # 基于文本哈希生成确定性随机向量（相同文本返回相同向量）
        seed = hash(text) % (2 ** 32)
        rng = random.Random(seed)
        return [rng.random() for _ in range(self._dimension)]

    async def embed_batch(self, texts: list[str]) -> list[list[float]]:
        return [await self.embed(t) for t in texts]
