from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Any, Dict, List

from qz.fact_checker import FactChecker
from qz.question_generator import QuestionGenerator
from qz.text_extractor import TextExtractor
from qz.text_preprocessor import TextPreprocessor


class PDFExtractor:
    """Handles PDF text extraction."""

    def __init__(self):
        """Initialize the PDF extractor with required components."""
        self.text_extractor = TextExtractor()
        self.text_preprocessor = TextPreprocessor()
        self.question_generator = QuestionGenerator()
        self.fact_checker = FactChecker()

    def generate_quiz(self, pdf_path: str, num_questions: int = 5) -> List[Dict[str, Any]]:
        """Generate a quiz from a PDF file.

        Args:
            pdf_path: Path to the PDF file
            num_questions: Number of questions to generate

        Returns:
            List of questions with their answers and options
        """
        # Extract text from PDF
        text = self.text_extractor.extract_text_from_pdf(pdf_path)
        if not text:
            return []

        # Preprocess text
        chunks = self.text_preprocessor.split_into_chunks(text)
        questions = []
        failed_chunks = []

        # Generate questions in parallel
        with ThreadPoolExecutor() as executor:
            futures = []
            for chunk in chunks[:num_questions]:
                futures.append(executor.submit(self.question_generator.create_quiz_question, chunk))

            for future in as_completed(futures):
                try:
                    question = future.result()
                    if question and self.fact_checker.validate_question(
                        question["text"], question["context"]
                    ):
                        questions.append(question)
                except Exception as e:
                    failed_chunks.append(str(e))
                    if len(failed_chunks) == len(chunks):
                        # If all chunks failed, something is seriously wrong
                        raise RuntimeError(
                            f"Failed to generate questions from any chunk. First error: {failed_chunks[0]}"
                        ) from e

        return questions[:num_questions]
