# xgen_doc2chunk/core/processor/ppt_handler.py
"""
PPT Handler - PPT/PPTX Document Processor

Class-based handler for PPT/PPTX files inheriting from BaseHandler.
"""
import logging
from typing import Any, Dict, List, Optional, Set, TYPE_CHECKING

from xgen_doc2chunk.core.processor.base_handler import BaseHandler
from xgen_doc2chunk.core.functions.chart_extractor import BaseChartExtractor
from xgen_doc2chunk.core.processor.ppt_helper import (
    ElementType,
    SlideElement,
    extract_text_with_bullets,
    is_simple_table,
    extract_simple_table_as_text,
    convert_table_to_html,
    extract_table_as_text,
    get_shape_position,
    is_picture_shape,
    process_image_shape,
    process_group_shape,
    extract_slide_notes,
    merge_slide_elements,
)
from xgen_doc2chunk.core.processor.ppt_helper.ppt_chart_extractor import PPTChartExtractor
from xgen_doc2chunk.core.processor.ppt_helper.ppt_metadata import PPTMetadataExtractor
from xgen_doc2chunk.core.processor.ppt_helper.ppt_image_processor import PPTImageProcessor

if TYPE_CHECKING:
    from xgen_doc2chunk.core.document_processor import CurrentFile
    from xgen_doc2chunk.core.functions.chart_extractor import ChartData

logger = logging.getLogger("document-processor")


class PPTHandler(BaseHandler):
    """PPT/PPTX File Processing Handler Class"""

    def _create_file_converter(self):
        """Create PPT-specific file converter."""
        from xgen_doc2chunk.core.processor.ppt_helper.ppt_file_converter import PPTFileConverter
        return PPTFileConverter()

    def _create_preprocessor(self):
        """Create PPT-specific preprocessor."""
        from xgen_doc2chunk.core.processor.ppt_helper.ppt_preprocessor import PPTPreprocessor
        return PPTPreprocessor()

    def _create_chart_extractor(self) -> BaseChartExtractor:
        """Create PPT-specific chart extractor."""
        return PPTChartExtractor(self._chart_processor)

    def _create_metadata_extractor(self):
        """Create PPT-specific metadata extractor."""
        return PPTMetadataExtractor()

    def _create_format_image_processor(self):
        """Create PPT-specific image processor."""
        return PPTImageProcessor(
            directory_path=self._image_processor.config.directory_path,
            tag_prefix=self._image_processor.config.tag_prefix,
            tag_suffix=self._image_processor.config.tag_suffix,
            storage_backend=self._image_processor.storage_backend,
        )

    def extract_text(
        self,
        current_file: "CurrentFile",
        extract_metadata: bool = True,
        **kwargs
    ) -> str:
        """
        Extract text from PPT/PPTX file.

        Args:
            current_file: CurrentFile dict containing file info and binary data
            extract_metadata: Whether to extract metadata
            **kwargs: Additional options

        Returns:
            Extracted text
        """
        file_path = current_file.get("file_path", "unknown")
        self.logger.info(f"PPT processing: {file_path}")
        return self._extract_ppt_enhanced(current_file, extract_metadata)

    def _extract_ppt_enhanced(self, current_file: "CurrentFile", extract_metadata: bool = True) -> str:
        """Enhanced PPT processing with pre-extracted charts."""
        file_path = current_file.get("file_path", "unknown")
        self.logger.info(f"Enhanced PPT processing: {file_path}")

        try:
            # Step 1: Convert to Presentation using file_converter
            file_data = current_file.get("file_data", b"")
            file_stream = self.get_file_stream(current_file)
            prs = self.file_converter.convert(file_data, file_stream)

            # Step 2: Preprocess - may transform prs in the future
            preprocessed = self.preprocess(prs)
            prs = preprocessed.clean_content  # TRUE SOURCE

            result_parts = []
            processed_images: Set[str] = set()
            total_tables = 0
            total_images = 0
            total_charts = 0

            # Pre-extract all charts using ChartExtractor
            file_stream.seek(0)
            chart_data_list = self.chart_extractor.extract_all_from_file(file_stream)
            chart_idx = [0]  # Mutable container for closure

            def get_next_chart() -> str:
                """Callback to get the next pre-extracted chart content."""
                if chart_idx[0] < len(chart_data_list):
                    chart_data = chart_data_list[chart_idx[0]]
                    chart_idx[0] += 1
                    return self._format_chart_data(chart_data)
                return ""

            if extract_metadata:
                metadata_text = self.extract_and_format_metadata(prs)
                if metadata_text:
                    result_parts.append(metadata_text)
                    result_parts.append("")

            for slide_idx, slide in enumerate(prs.slides):
                slide_tag = self.create_slide_tag(slide_idx + 1)
                result_parts.append(f"\n{slide_tag}\n")

                elements: List[SlideElement] = []

                for shape in slide.shapes:
                    try:
                        position = get_shape_position(shape)
                        shape_id = shape.shape_id if hasattr(shape, 'shape_id') else id(shape)

                        if shape.has_table:
                            if is_simple_table(shape.table):
                                simple_text = extract_simple_table_as_text(shape.table)
                                if simple_text:
                                    elements.append(SlideElement(
                                        element_type=ElementType.TEXT,
                                        content=simple_text,
                                        position=position,
                                        shape_id=shape_id
                                    ))
                            else:
                                table_html = convert_table_to_html(shape.table)
                                if table_html:
                                    total_tables += 1
                                    elements.append(SlideElement(
                                        element_type=ElementType.TABLE,
                                        content=table_html,
                                        position=position,
                                        shape_id=shape_id
                                    ))

                        elif is_picture_shape(shape):
                            image_tag = process_image_shape(shape, processed_images, self.format_image_processor)
                            if image_tag:
                                total_images += 1
                                elements.append(SlideElement(
                                    element_type=ElementType.IMAGE,
                                    content=image_tag,
                                    position=position,
                                    shape_id=shape_id
                                ))

                        elif shape.has_chart:
                            # Use pre-extracted chart via callback
                            chart_text = get_next_chart()
                            if chart_text:
                                total_charts += 1
                                elements.append(SlideElement(
                                    element_type=ElementType.CHART,
                                    content=chart_text,
                                    position=position,
                                    shape_id=shape_id
                                ))

                        elif hasattr(shape, "text_frame") and shape.text_frame:
                            text_content = extract_text_with_bullets(shape.text_frame)
                            if text_content:
                                elements.append(SlideElement(
                                    element_type=ElementType.TEXT,
                                    content=text_content,
                                    position=position,
                                    shape_id=shape_id
                                ))

                        elif hasattr(shape, "text") and shape.text.strip():
                            elements.append(SlideElement(
                                element_type=ElementType.TEXT,
                                content=shape.text.strip(),
                                position=position,
                                shape_id=shape_id
                            ))

                        elif hasattr(shape, "shapes"):
                            group_elements = process_group_shape(shape, processed_images, self.format_image_processor)
                            elements.extend(group_elements)

                    except Exception as shape_e:
                        self.logger.warning(f"Error processing shape in slide {slide_idx + 1}: {shape_e}")
                        continue

                elements.sort(key=lambda e: e.sort_key)
                slide_content = merge_slide_elements(elements)

                if slide_content.strip():
                    result_parts.append(slide_content)
                else:
                    result_parts.append("[Empty Slide]\n")

                notes_text = extract_slide_notes(slide)
                if notes_text:
                    result_parts.append(f"\n[Slide Notes]\n{notes_text}\n")

            result = "".join(result_parts)
            self.logger.info(f"Enhanced PPT: {len(prs.slides)} slides, {total_tables} tables, "
                           f"{total_images} images, {total_charts} charts")

            return result

        except Exception as e:
            self.logger.error(f"Error in enhanced PPT processing: {e}")
            import traceback
            self.logger.debug(traceback.format_exc())
            return self._extract_ppt_simple(current_file)

    def _format_chart_data(self, chart_data: "ChartData") -> str:
        """Format ChartData using ChartProcessor."""
        from xgen_doc2chunk.core.functions.chart_extractor import ChartData

        if not isinstance(chart_data, ChartData):
            return ""

        if chart_data.has_data():
            return self.chart_processor.format_chart_data(
                chart_type=chart_data.chart_type,
                title=chart_data.title,
                categories=chart_data.categories,
                series=chart_data.series
            )
        else:
            return self.chart_processor.format_chart_fallback(
                chart_type=chart_data.chart_type,
                title=chart_data.title
            )

    def _extract_ppt_simple(self, current_file: "CurrentFile") -> str:
        """Simple text extraction (fallback)."""
        try:
            file_data = current_file.get("file_data", b"")
            file_stream = self.get_file_stream(current_file)
            prs = self.file_converter.convert(file_data, file_stream)
            result_parts = []

            for slide_idx, slide in enumerate(prs.slides):
                slide_tag = self.create_slide_tag(slide_idx + 1)
                result_parts.append(f"\n{slide_tag}\n")

                slide_texts = []
                for shape in slide.shapes:
                    try:
                        if hasattr(shape, "text") and shape.text.strip():
                            slide_texts.append(shape.text.strip())
                        elif hasattr(shape, "table"):
                            table_text = extract_table_as_text(shape.table)
                            if table_text:
                                slide_texts.append(table_text)
                    except:
                        continue

                if slide_texts:
                    result_parts.append("\n".join(slide_texts) + "\n")
                else:
                    result_parts.append("[Empty Slide]\n")

            return "".join(result_parts)

        except Exception as e:
            self.logger.error(f"Error in simple PPT extraction: {e}")
            return f"[PPT file processing failed: {str(e)}]"

