"""
Format detection and classification for OOXML and ODF files.
Identifies specific Office formats and their capabilities.
"""

import zipfile
from pathlib import Path
from typing import Dict, List, Optional, Set
from dataclasses import dataclass
from enum import Enum

from .xml_parser import XMLParser


class OfficeFormat(Enum):
    """Office format types."""

    # OOXML formats
    POWERPOINT_TEMPLATE = "powerpoint_template"  # .potx
    POWERPOINT_PRESENTATION = "powerpoint_presentation"  # .pptx
    WORD_TEMPLATE = "word_template"  # .dotx
    WORD_DOCUMENT = "word_document"  # .docx
    EXCEL_TEMPLATE = "excel_template"  # .xltx
    EXCEL_WORKBOOK = "excel_workbook"  # .xlsx

    # ODF formats
    IMPRESS_TEMPLATE = "impress_template"  # .otp
    IMPRESS_PRESENTATION = "impress_presentation"  # .odp
    WRITER_TEMPLATE = "writer_template"  # .ott
    WRITER_DOCUMENT = "writer_document"  # .odt
    CALC_TEMPLATE = "calc_template"  # .ots
    CALC_SPREADSHEET = "calc_spreadsheet"  # .ods
    DRAW_DOCUMENT = "draw_document"  # .odg
    FORMULA_DOCUMENT = "formula_document"  # .odf

    # Generic
    UNKNOWN = "unknown"


@dataclass
class FormatInfo:
    """Detailed information about Office format."""

    format_type: OfficeFormat
    primary_format: str  # 'presentation', 'document', 'spreadsheet'
    is_template: bool
    standard: str  # 'ooxml' or 'odf'
    content_types: Set[str]
    main_xml_files: List[str]
    version_info: Optional[str] = None
    application_info: Optional[str] = None


class FormatDetector:
    """Detection and classification of OOXML and ODF formats."""

    def __init__(self):
        """Initialize format detector."""
        self.parser = XMLParser()

        # File extension mappings
        self.extension_map = {
            ".potx": OfficeFormat.POWERPOINT_TEMPLATE,
            ".pptx": OfficeFormat.POWERPOINT_PRESENTATION,
            ".dotx": OfficeFormat.WORD_TEMPLATE,
            ".docx": OfficeFormat.WORD_DOCUMENT,
            ".xltx": OfficeFormat.EXCEL_TEMPLATE,
            ".xlsx": OfficeFormat.EXCEL_WORKBOOK,
            ".otp": OfficeFormat.IMPRESS_TEMPLATE,
            ".odp": OfficeFormat.IMPRESS_PRESENTATION,
            ".ott": OfficeFormat.WRITER_TEMPLATE,
            ".odt": OfficeFormat.WRITER_DOCUMENT,
            ".ots": OfficeFormat.CALC_TEMPLATE,
            ".ods": OfficeFormat.CALC_SPREADSHEET,
            ".odg": OfficeFormat.DRAW_DOCUMENT,
            ".odf": OfficeFormat.FORMULA_DOCUMENT,
        }

        # Content type signatures for OOXML
        self.ooxml_signatures = {
            "presentation": [
                "application/vnd.openxmlformats-presentationml.presentation.main+xml",
                "application/vnd.openxmlformats-presentationml.template.main+xml",
            ],
            "document": [
                "application/vnd.openxmlformats-wordprocessingml.document.main+xml",
                "application/vnd.openxmlformats-wordprocessingml.template.main+xml",
            ],
            "spreadsheet": [
                "application/vnd.openxmlformats-spreadsheetml.sheet.main+xml",
                "application/vnd.openxmlformats-spreadsheetml.template.main+xml",
            ],
        }

        # ODF MIME types
        self.odf_mimetypes = {
            "presentation": [
                "application/vnd.oasis.opendocument.presentation",
                "application/vnd.oasis.opendocument.presentation-template",
            ],
            "document": [
                "application/vnd.oasis.opendocument.text",
                "application/vnd.oasis.opendocument.text-template",
            ],
            "spreadsheet": [
                "application/vnd.oasis.opendocument.spreadsheet",
                "application/vnd.oasis.opendocument.spreadsheet-template",
            ],
            "graphics": [
                "application/vnd.oasis.opendocument.graphics",
                "application/vnd.oasis.opendocument.graphics-template",
            ],
            "formula": [
                "application/vnd.oasis.opendocument.formula",
                "application/vnd.oasis.opendocument.formula-template",
            ],
        }

    def detect_format(self, file_path: Path) -> OfficeFormat:
        """Detect Office format from file path."""
        file_path = Path(file_path)
        extension = file_path.suffix.lower()
        return self.extension_map.get(extension, OfficeFormat.UNKNOWN)

    def analyze_format_detailed(self, file_path: Path) -> FormatInfo:
        """Perform detailed format analysis."""
        file_path = Path(file_path)
        basic_format = self.detect_format(file_path)

        format_info = FormatInfo(
            format_type=basic_format,
            primary_format=self._get_primary_format(basic_format),
            is_template=self._is_template_format(basic_format),
            standard=self._get_standard(basic_format),
            content_types=set(),
            main_xml_files=[],
        )

        if basic_format == OfficeFormat.UNKNOWN:
            return format_info

        try:
            if format_info.standard == "ooxml":
                self._analyze_ooxml_format(file_path, format_info)
            elif format_info.standard == "odf":
                self._analyze_odf_format(file_path, format_info)

        except Exception:
            # Return basic format info if detailed analysis fails
            pass

        return format_info

    def _get_primary_format(self, office_format: OfficeFormat) -> str:
        """Get primary format category."""
        presentation_formats = {
            OfficeFormat.POWERPOINT_TEMPLATE,
            OfficeFormat.POWERPOINT_PRESENTATION,
            OfficeFormat.IMPRESS_TEMPLATE,
            OfficeFormat.IMPRESS_PRESENTATION,
        }
        document_formats = {
            OfficeFormat.WORD_TEMPLATE,
            OfficeFormat.WORD_DOCUMENT,
            OfficeFormat.WRITER_TEMPLATE,
            OfficeFormat.WRITER_DOCUMENT,
        }
        spreadsheet_formats = {
            OfficeFormat.EXCEL_TEMPLATE,
            OfficeFormat.EXCEL_WORKBOOK,
            OfficeFormat.CALC_TEMPLATE,
            OfficeFormat.CALC_SPREADSHEET,
        }

        if office_format in presentation_formats:
            return "presentation"
        elif office_format in document_formats:
            return "document"
        elif office_format in spreadsheet_formats:
            return "spreadsheet"
        elif office_format == OfficeFormat.DRAW_DOCUMENT:
            return "graphics"
        elif office_format == OfficeFormat.FORMULA_DOCUMENT:
            return "formula"
        else:
            return "unknown"

    def _is_template_format(self, office_format: OfficeFormat) -> bool:
        """Check if format is a template."""
        template_formats = {
            OfficeFormat.POWERPOINT_TEMPLATE,
            OfficeFormat.WORD_TEMPLATE,
            OfficeFormat.EXCEL_TEMPLATE,
            OfficeFormat.IMPRESS_TEMPLATE,
            OfficeFormat.WRITER_TEMPLATE,
            OfficeFormat.CALC_TEMPLATE,
        }
        return office_format in template_formats

    def _get_standard(self, office_format: OfficeFormat) -> str:
        """Get format standard (ooxml or odf)."""
        ooxml_formats = {
            OfficeFormat.POWERPOINT_TEMPLATE,
            OfficeFormat.POWERPOINT_PRESENTATION,
            OfficeFormat.WORD_TEMPLATE,
            OfficeFormat.WORD_DOCUMENT,
            OfficeFormat.EXCEL_TEMPLATE,
            OfficeFormat.EXCEL_WORKBOOK,
        }
        if office_format in ooxml_formats:
            return "ooxml"
        odf_formats = {
            OfficeFormat.IMPRESS_TEMPLATE,
            OfficeFormat.IMPRESS_PRESENTATION,
            OfficeFormat.WRITER_TEMPLATE,
            OfficeFormat.WRITER_DOCUMENT,
            OfficeFormat.CALC_TEMPLATE,
            OfficeFormat.CALC_SPREADSHEET,
            OfficeFormat.DRAW_DOCUMENT,
            OfficeFormat.FORMULA_DOCUMENT,
        }
        if office_format in odf_formats:
            return "odf"
        return "unknown"

    def _analyze_ooxml_format(self, file_path: Path, format_info: FormatInfo):
        """Analyze OOXML format details."""
        with zipfile.ZipFile(file_path, "r") as zf:
            # Analyze Content_Types.xml
            if "[Content_Types].xml" in zf.namelist():
                content_types_xml = zf.read("[Content_Types].xml").decode("utf-8")
                self._parse_content_types(content_types_xml, format_info)

            # Analyze app.xml for application info
            if "docProps/app.xml" in zf.namelist():
                app_xml = zf.read("docProps/app.xml").decode("utf-8")
                self._parse_app_properties(app_xml, format_info)

            # Identify main XML files
            format_info.main_xml_files = self._get_ooxml_main_files(
                zf.namelist(), format_info.primary_format
            )

    def _analyze_odf_format(self, file_path: Path, format_info: FormatInfo):
        """Analyze ODF format details."""
        with zipfile.ZipFile(file_path, "r") as zf:
            # Analyze META-INF/manifest.xml
            if "META-INF/manifest.xml" in zf.namelist():
                manifest_xml = zf.read("META-INF/manifest.xml").decode("utf-8")
                self._parse_odf_manifest(manifest_xml, format_info)

            # Analyze meta.xml for version info
            if "meta.xml" in zf.namelist():
                meta_xml = zf.read("meta.xml").decode("utf-8")
                self._parse_odf_meta(meta_xml, format_info)

            # Identify main XML files
            format_info.main_xml_files = self._get_odf_main_files(zf.namelist())

    def _parse_content_types(self, content_types_xml: str, format_info: FormatInfo):
        """Parse OOXML Content_Types.xml."""
        try:
            structure = self.parser.analyze_structure(content_types_xml)

            for elem in structure.root.iter():
                if elem.tag.endswith("}Override") or elem.tag == "Override":
                    content_type = elem.get("ContentType", "")
                    if content_type:
                        format_info.content_types.add(content_type)

        except Exception:
            pass

    def _parse_app_properties(self, app_xml: str, format_info: FormatInfo):
        """Parse OOXML app.xml for application information."""
        try:
            structure = self.parser.analyze_structure(app_xml)

            for elem in structure.root.iter():
                if elem.tag.endswith("}Application") or elem.tag == "Application":
                    if elem.text:
                        format_info.application_info = elem.text.strip()
                elif elem.tag.endswith("}AppVersion") or elem.tag == "AppVersion":
                    if elem.text:
                        format_info.version_info = elem.text.strip()

        except Exception:
            pass

    def _parse_odf_manifest(self, manifest_xml: str, format_info: FormatInfo):
        """Parse ODF manifest.xml."""
        try:
            structure = self.parser.analyze_structure(manifest_xml)

            for elem in structure.root.iter():
                if elem.tag.endswith("}file-entry") or elem.tag == "file-entry":
                    media_type = elem.get("media-type", "")
                    if media_type:
                        format_info.content_types.add(media_type)

        except Exception:
            pass

    def _parse_odf_meta(self, meta_xml: str, format_info: FormatInfo):
        """Parse ODF meta.xml for version information."""
        try:
            structure = self.parser.analyze_structure(meta_xml)

            for elem in structure.root.iter():
                if elem.tag.endswith("}generator") or elem.tag == "generator":
                    if elem.text:
                        format_info.application_info = elem.text.strip()

        except Exception:
            pass

    def _get_ooxml_main_files(
        self, file_list: List[str], primary_format: str
    ) -> List[str]:
        """Get main XML files for OOXML format."""
        main_files = ["[Content_Types].xml", "_rels/.rels"]

        if primary_format == "presentation":
            main_files.extend(
                [
                    "ppt/presentation.xml",
                    "ppt/theme/theme1.xml",
                    "ppt/slideMasters/slideMaster1.xml",
                ]
            )
        elif primary_format == "document":
            main_files.extend(
                ["word/document.xml", "word/styles.xml", "word/theme/theme1.xml"]
            )
        elif primary_format == "spreadsheet":
            main_files.extend(
                ["xl/workbook.xml", "xl/styles.xml", "xl/theme/theme1.xml"]
            )

        # Filter to only include files that actually exist
        return [f for f in main_files if f in file_list]

    def _get_odf_main_files(self, file_list: List[str]) -> List[str]:
        """Get main XML files for ODF format."""
        main_files = [
            "content.xml",
            "styles.xml",
            "meta.xml",
            "settings.xml",
            "META-INF/manifest.xml",
        ]
        return [f for f in main_files if f in file_list]

    def analyze_content_types(self, content_types_xml: str) -> FormatInfo:
        """Analyze [Content_Types].xml content directly."""
        format_info = FormatInfo(
            format_type=OfficeFormat.UNKNOWN,
            primary_format="unknown",
            is_template=False,
            standard="ooxml",
            content_types=set(),
            main_xml_files=[],
        )

        self._parse_content_types(content_types_xml, format_info)

        # Determine primary format from content types
        for content_type in format_info.content_types:
            for format_name, signatures in self.ooxml_signatures.items():
                if any(sig in content_type for sig in signatures):
                    format_info.primary_format = format_name
                    format_info.is_template = "template" in content_type
                    break

        return format_info

    def get_format_capabilities(self, office_format: OfficeFormat) -> Dict[str, bool]:
        """Get capabilities supported by the format."""
        capabilities = {
            "supports_themes": False,
            "supports_styles": False,
            "supports_charts": False,
            "supports_shapes": False,
            "supports_tables": False,
            "supports_images": False,
            "supports_animations": False,
            "supports_hyperlinks": False,
            "supports_comments": False,
            "supports_formulas": False,
            "supports_macros": False,
            "supports_custom_xml": False,
        }

        # Presentation formats
        if office_format in [
            OfficeFormat.POWERPOINT_TEMPLATE,
            OfficeFormat.POWERPOINT_PRESENTATION,
            OfficeFormat.IMPRESS_TEMPLATE,
            OfficeFormat.IMPRESS_PRESENTATION,
        ]:
            capabilities.update(
                {
                    "supports_themes": True,
                    "supports_styles": True,
                    "supports_charts": True,
                    "supports_shapes": True,
                    "supports_tables": True,
                    "supports_images": True,
                    "supports_animations": True,
                    "supports_hyperlinks": True,
                    "supports_comments": True,
                    "supports_custom_xml": office_format.value.startswith("powerpoint"),
                }
            )

        # Document formats
        elif office_format in [
            OfficeFormat.WORD_TEMPLATE,
            OfficeFormat.WORD_DOCUMENT,
            OfficeFormat.WRITER_TEMPLATE,
            OfficeFormat.WRITER_DOCUMENT,
        ]:
            capabilities.update(
                {
                    "supports_themes": True,
                    "supports_styles": True,
                    "supports_charts": True,
                    "supports_shapes": True,
                    "supports_tables": True,
                    "supports_images": True,
                    "supports_hyperlinks": True,
                    "supports_comments": True,
                    "supports_custom_xml": office_format.value.startswith("word"),
                }
            )

        # Spreadsheet formats
        elif office_format in [
            OfficeFormat.EXCEL_TEMPLATE,
            OfficeFormat.EXCEL_WORKBOOK,
            OfficeFormat.CALC_TEMPLATE,
            OfficeFormat.CALC_SPREADSHEET,
        ]:
            capabilities.update(
                {
                    "supports_themes": True,
                    "supports_styles": True,
                    "supports_charts": True,
                    "supports_shapes": True,
                    "supports_tables": True,
                    "supports_images": True,
                    "supports_hyperlinks": True,
                    "supports_comments": True,
                    "supports_formulas": True,
                    "supports_macros": office_format.value.startswith("excel"),
                    "supports_custom_xml": office_format.value.startswith("excel"),
                }
            )

        return capabilities
