from typing import Optional, List, Dict, Any
import numpy as np
from openai import AsyncOpenAI

from .embeddings import embed
from .tda import detect_holes
from .labeler import label_holes
from .utils import split_text
from .scraper import scrape

class TopoAnalyzer:
    """
    Topological RAG Analyzer.
    Detects conceptual gaps and blind spots in text documents.
    """
    def __init__(self, openai_api_key: Optional[str] = None):
        """
        Initialize the analyzer.
        :param openai_api_key: OpenAI API key. If None, expects OPENAI_API_KEY environment variable.
        """
        self.client = AsyncOpenAI(api_key=openai_api_key)

    async def analyze_url(
        self,
        url: str,
        threshold: float = 0.15,
        max_holes: int = 5,
        generate_suggestions: bool = True
    ) -> List[Dict[str, Any]]:
        """
        Scrape a URL and analyze it for topological gaps.
        """
        text = await scrape(url)
        return await self.analyze_text(
            text=text,
            threshold=threshold,
            max_holes=max_holes,
            generate_suggestions=generate_suggestions
        )

    async def analyze_text(
        self,
        text: str,
        threshold: float = 0.15,
        max_holes: int = 5,
        generate_suggestions: bool = True
    ) -> List[Dict[str, Any]]:
        """
        Analyze a raw text string for topological gaps.
        """
        segments = split_text(text)
        if len(segments) < 3:
            raise ValueError("Input text must contain at least 3 valid segments (paragraphs/sentences) for topological analysis.")
        
        return await self.analyze_segments(
            segments=segments,
            threshold=threshold,
            max_holes=max_holes,
            generate_suggestions=generate_suggestions
        )

    async def analyze_segments(
        self,
        segments: List[str],
        threshold: float = 0.15,
        max_holes: int = 5,
        generate_suggestions: bool = True
    ) -> List[Dict[str, Any]]:
        """
        Analyze a list of text segments for topological gaps.
        """
        if len(segments) < 3:
            raise ValueError("Must provide at least 3 segments for topological analysis.")

        # 1. Embeddings
        embeddings_list = await embed(client=self.client, texts=segments)
        embeddings_np = np.array(embeddings_list)
        
        # 2. TDA (Gap Detection)
        holes = detect_holes(
            embeddings=embeddings_np, 
            threshold=threshold, 
            max_holes=max_holes
        )
        
        if not holes:
            return []
            
        # 3. Format metadata for labeler
        metadata = [{"content": seg} for seg in segments]
        
        # 4. Label the gaps using LLM
        labeled_gaps = await label_holes(
            client=self.client,
            holes=holes,
            embeddings=embeddings_np,
            metadata=metadata,
            generate_suggestions=generate_suggestions
        )
        
        return labeled_gaps
