# app/lib/embedding_providers/claude_provider.py
from typing import List, Optional
from anthropic import Anthropic
from openai import OpenAI
from .base import BaseProvider


class ClaudeProvider(BaseProvider):
    """
    Claude provider for completions with OpenAI embeddings fallback.

    Note: Claude (Anthropic) does not provide a native embeddings API.
    This provider uses OpenAI for embeddings and Claude for completions.
    """

    def __init__(
        self,
        claude_api_key: str,
        openai_api_key: Optional[str] = None,
        completion_model: str = "claude-3-5-sonnet-20241022",
        embedding_model: str = "text-embedding-3-small"
    ):
        """
        Initialize Claude provider.

        Args:
            claude_api_key: Anthropic API key for Claude
            openai_api_key: OpenAI API key for embeddings (optional if only using completions)
            completion_model: Claude model to use for completions
            embedding_model: OpenAI model to use for embeddings
        """
        if not claude_api_key:
            raise ValueError("Claude API key is required")

        self.claude_client = Anthropic(api_key=claude_api_key)
        self.completion_model = completion_model
        self.embedding_model = embedding_model
        self._dimension = 1536  # Default for text-embedding-3-small

        # Initialize OpenAI client for embeddings if key provided
        self.openai_client = None
        if openai_api_key:
            self.openai_client = OpenAI(api_key=openai_api_key)

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

    @property
    def provider_name(self) -> str:
        return "claude"

    @property
    def model_name(self) -> str:
        return self.completion_model

    def supports_embeddings(self) -> bool:
        """Claude uses OpenAI fallback for embeddings"""
        return self.openai_client is not None

    def get_embeddings(self, texts: List[str]) -> List[List[float]]:
        """
        Generate embeddings using OpenAI API (fallback).

        Args:
            texts: List of text strings to embed

        Returns:
            List of embedding vectors

        Raises:
            ValueError: If texts is empty
            RuntimeError: If API call fails or OpenAI client not configured
        """
        if not self.openai_client:
            raise RuntimeError(
                "OpenAI API key not configured. Claude provider requires OpenAI for embeddings."
            )

        if not texts:
            raise ValueError("Text list cannot be empty")

        try:
            response = self.openai_client.embeddings.create(
                model=self.embedding_model,
                input=texts
            )
            return [item.embedding for item in response.data]
        except Exception as e:
            raise RuntimeError(f"OpenAI embedding API call failed: {str(e)}")

    def get_completion(self, prompt: str, context: str = "") -> str:
        """
        Generate a completion using Claude (Anthropic) API.

        Args:
            prompt: The user's question
            context: Optional context from retrieved documents

        Returns:
            The generated response

        Raises:
            ValueError: If prompt is empty
            RuntimeError: If API call fails
        """
        if not prompt:
            raise ValueError("Prompt cannot be empty")

        try:
            # Build system message
            system_message = ""
            if context:
                system_message = (
                    "Answer the user's question based on the following context. "
                    "If the context doesn't contain relevant information, say so.\n\n"
                    f"Context:\n{context}"
                )

            # Call Claude API
            response = self.claude_client.messages.create(
                model=self.completion_model,
                max_tokens=2000,
                system=system_message if system_message else None,
                messages=[
                    {
                        "role": "user",
                        "content": prompt
                    }
                ]
            )

            # Extract text from response
            return response.content[0].text

        except Exception as e:
            raise RuntimeError(f"Claude completion API call failed: {str(e)}")
