# pylint: disable=import-outside-toplevel,too-many-positional-arguments

import logging
import os
import tempfile
from pathlib import Path
from typing import Generic, List, Optional, Type, TypeVar

from pydantic import BaseModel

from mini_videorag.core.pipeline import ProcessorPipeline, PipelineResult
from mini_videorag.core.video_rag_session import VideoRAGSession
from mini_videorag.types import VideoRAGAnswer, VideoRAGSessionConfig

logger = logging.getLogger(__name__)

T = TypeVar("T", bound=BaseModel)


class VideoRAG(Generic[T]):
    """
    Video RAG context manager with automatic resource cleanup.

    This is a thin wrapper around VideoRAGSession that handles:
    - Temporary directory creation/cleanup
    - Debug mode (preserving directories for inspection)
    - Automatic resource lifecycle management

    For manual control without context manager, use VideoRAGSession directly.

    Example (Context Manager - Automatic Cleanup):
        with VideoRAG(pipeline) as rag:
            rag.init("./video.mp4")
            answer = rag.ask("Does this have subtitles?")
        # Directories automatically cleaned up

    Example (Custom Output Schema):
        class CustomAnswer(BaseModel):
            answer: str
            confidence_score: float
            reasoning: str

        with VideoRAG(pipeline, output_schema=CustomAnswer) as rag:
            rag.init("./video.mp4")
            answer = rag.ask("Does this have subtitles?")  # Returns CustomAnswer

    Example (Debug Mode):
        with VideoRAG(pipeline, debug=True) as rag:
            rag.init("./video.mp4")
            answer = rag.ask("question")
        # Directories preserved in logs/debug/ for inspection
    """

    def __init__(
        self,
        pipeline: ProcessorPipeline,
        output_schema: Type[T] = VideoRAGAnswer,
        session_config: Optional[VideoRAGSessionConfig] = None,
        debug: bool = False,
        config: Optional[dict] = None,
    ):
        """
        Initialize Video RAG context manager.

        Args:
            pipeline: ProcessorPipeline for video analysis
            output_schema: Pydantic model for structured output
            session_config: VideoRAGSessionConfig for all settings (uses defaults if None)
            debug: If True, preserve temp folders for inspection
            config: Optional full config dict (for logging)
        """
        self._session = VideoRAGSession(
            pipeline=pipeline,
            output_schema=output_schema,
            session_config=session_config,
            config=config,
        )

        self.debug = debug
        self.root_dir: Optional[tempfile.TemporaryDirectory] = None
        self.video_path: Optional[str] = None
        self.frames_dir: Optional[str] = None
        self.lance_db_text: Optional[str] = None
        self.lance_db_image: Optional[str] = None

    def __repr__(self) -> str:
        """Return a developer-friendly string representation."""
        return f"VideoRAG(" f"session={repr(self._session)}, " f"debug={self.debug}" f")"

    def __str__(self) -> str:
        """Return a user-friendly string representation."""
        return str(self._session)

    def __enter__(self):
        """Setup temporary directories for video processing."""
        if self.debug:
            import time

            logs_debug_dir = "logs/debug"
            os.makedirs(logs_debug_dir, exist_ok=True)
            debug_dir = os.path.join(logs_debug_dir, f"debug_videorag_{int(time.time())}")
            os.makedirs(debug_dir, exist_ok=True)
            self.root_dir = type("obj", (object,), {"name": debug_dir})()
            logger.info(f"[DEBUG] Using persistent directory: {debug_dir}")
        else:
            self.root_dir = tempfile.TemporaryDirectory()

        self.video_path = os.path.join(self.root_dir.name, "video.mp4")
        self.frames_dir = os.path.join(self.root_dir.name, "frames")
        self.lance_db_text = os.path.join(self.root_dir.name, "lance_db_text")
        self.lance_db_image = os.path.join(self.root_dir.name, "lance_db_image")

        Path(self.frames_dir).mkdir(parents=True, exist_ok=True)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Cleanup resources and optionally preserve debug directories."""
        self._session.cleanup()

        if self.debug and self.root_dir:
            logger.info("=" * 80)
            logger.info(f"[DEBUG] 🔍 TEMP DIRECTORY PRESERVED AT: {self.root_dir.name}")
            logger.info(f"[DEBUG] Frames: {self.frames_dir}")
            logger.info(f"[DEBUG] LanceDB Text: {self.lance_db_text}")
            logger.info(f"[DEBUG] LanceDB Image: {self.lance_db_image}")
            logger.info("=" * 80)
        elif self.root_dir and hasattr(self.root_dir, "cleanup"):
            self.root_dir.cleanup()

        if not self.debug:
            self.root_dir = None
            self.video_path = None
            self.frames_dir = None
            self.lance_db_text = None
            self.lance_db_image = None

    @classmethod
    def from_config(
        cls,
        pipeline: ProcessorPipeline,
        config_path: Optional[str] = None,
        config_dict: Optional[dict] = None,
    ) -> "VideoRAG":
        """
        Create VideoRAG instance from configuration file or dict.

        Args:
            pipeline: ProcessorPipeline instance
            config_path: Path to video_rag_config.yml
            config_dict: Config dict (alternative to config_path)

        Returns:
            Configured VideoRAG instance

        Example:
            config = load_video_rag_config()
            pipeline = create_pipeline_from_config(config)
            rag = VideoRAG.from_config(pipeline, config_dict=config)
        """
        if config_dict is None:
            from mini_videorag.utils.config_loader import load_video_rag_config

            config_dict = load_video_rag_config(config_path)

        session_config = VideoRAGSessionConfig.from_dict(config_dict)

        rag_config = config_dict.get("video_rag", {})
        debug = rag_config.get("debug", False)

        return cls(
            pipeline=pipeline,
            config=config_dict,
            session_config=session_config,
            debug=debug,
        )

    def init(
        self,
        video_path: str,
        num_frames: int = 10,
        run_id: str = None,
    ) -> PipelineResult:
        """
        Initialize video analysis and vector stores.

        Args:
            video_path: Path or URL to video file
            num_frames: Number of frames to extract
            run_id: Optional run identifier for logging

        Returns:
            PipelineResult with all processor outputs
        """
        if not self.root_dir:
            raise RuntimeError("Must be used as context manager (with VideoRAG(...) as rag:)")

        return self._session.initialize(
            video_path=video_path,
            frames_dir=self.frames_dir,
            lance_db_text_path=self.lance_db_text,
            lance_db_image_path=self.lance_db_image,
            video_dest_path=self.video_path,
            num_frames=num_frames,
            run_id=run_id,
        )

    def ask(self, question: str, use_hybrid_search: bool = False) -> T:
        """
        Ask a question about the video.

        Args:
            question: Question to answer
            use_hybrid_search: If True, use hybrid search (vector + text)

        Returns:
            Structured answer using output_schema
        """
        return self._session.ask(question, use_hybrid_search)

    def ask_with_retrieval(self, question: str, retrieval_results: List) -> T:
        """
        Ask a question using pre-retrieved results.

        Args:
            question: Question to answer
            retrieval_results: Pre-retrieved nodes from search_lancedb()

        Returns:
            Structured answer using output_schema
        """
        return self._session.ask_with_retrieval(question, retrieval_results)

    def search_lancedb(
        self,
        question: str,
        use_hybrid: bool = True,
        rerank: bool = True,
    ) -> List:
        """
        Search LanceDB using hybrid search (vector + text).

        Args:
            question: Search query
            use_hybrid: If True, use hybrid search
            rerank: If True, apply reranking

        Returns:
            List of retrieval results
        """
        return self._session.search_lancedb(question, use_hybrid, rerank)

    @property
    def ready(self) -> bool:
        """Check if session is ready."""
        return self._session.ready

    @property
    def metadata(self) -> Optional[PipelineResult]:
        """Get pipeline metadata."""
        return self._session.metadata

    @property
    def retriever_engine(self):
        """Get retriever engine."""
        return self._session.retriever_engine

    @property
    def output_dir(self) -> Optional[str]:
        """Get output directory."""
        return self._session.output_dir

    @property
    def question_counter(self) -> int:
        """Get question counter."""
        return self._session.question_counter

    @property
    def pipeline(self) -> ProcessorPipeline:
        """Get pipeline."""
        return self._session.pipeline
