#!/usr/bin/env python3
"""
Grammar Metadata Loader - Loads all *.grammar.info.json files
Loads and merges all *.grammar.info.json files from the grammars directory.
Provides lookups by file extension, MIME type, alias, and format ID.
Company: eXonware.com
Author: eXonware Backend Team
Email: connect@exonware.com
Version: 0.6.0.19
Generation Date: 29-Oct-2025
"""

import json
from typing import Any
from pathlib import Path


class GrammarMetadata:
    """
    Grammar metadata manager that loads all *.grammar.info.json files.
    Loads all individual info.json files and merges them in memory.
    Provides:
    - Lookup by file extension
    - Lookup by MIME type
    - Lookup by alias
    - Lookup by format ID
    - Complete metadata retrieval
    """

    def __init__(self, grammar_dir: str | Path | None = None):
        """
        Initialize metadata loader.
        Args:
            grammar_dir: Directory containing *.grammar.info.json files (None = auto-detect)
        """
        if grammar_dir is None:
            # Auto-detect: look in grammars directory
            grammar_dir = Path(__file__).parent / 'grammars'
        self.grammar_dir = Path(grammar_dir)
        self._data: dict[str, dict[str, Any]] = {}
        self._extension_map: dict[str, str] = {}  # .ext -> format_id
        self._mime_map: dict[str, str] = {}  # mime_type -> format_id
        self._alias_map: dict[str, str] = {}  # alias -> format_id
        self._load()

    def _load(self) -> None:
        """Load and merge all *.grammar.info.json files."""
        if not self.grammar_dir.exists():
            # If directory doesn't exist, just return empty data
            return
        # Find all *.grammar.info.json files
        info_files = list(self.grammar_dir.glob("*.grammar.info.json"))
        # Load and merge all info.json files
        for info_file in info_files:
            try:
                with open(info_file, encoding='utf-8') as f:
                    metadata = json.load(f)
                # Extract format_id from metadata (use format_id or syntax_name as key)
                format_id = metadata.get('format_id', metadata.get('syntax_name', '').upper())
                # Store metadata (use format_id as key)
                if format_id:
                    self._data[format_id] = metadata
                    # Build indexes
                    # Index by file extensions
                    for ext in metadata.get('file_extensions', []):
                        ext_lower = ext.lower()
                        if not ext_lower.startswith('.'):
                            ext_lower = f'.{ext_lower}'
                        # Handle conflicts: first one wins
                        if ext_lower not in self._extension_map:
                            self._extension_map[ext_lower] = format_id
                    # Index by MIME types
                    for mime in metadata.get('mime_types', []):
                        mime_lower = mime.lower()
                        # Handle conflicts: first one wins
                        if mime_lower not in self._mime_map:
                            self._mime_map[mime_lower] = format_id
                    # Index by aliases
                    for alias in metadata.get('aliases', []):
                        alias_lower = alias.lower()
                        # Handle conflicts: first one wins
                        if alias_lower not in self._alias_map:
                            self._alias_map[alias_lower] = format_id
                    # Also index by format_id and syntax_name
                    self._alias_map[format_id.lower()] = format_id
                    syntax_name = metadata.get('syntax_name', '').lower()
                    if syntax_name:
                        self._alias_map[syntax_name] = format_id
            except Exception:
                # Skip invalid files - continue loading others
                continue

    def get_metadata(self, format_id: str) -> dict[str, Any] | None:
        """
        Get metadata for a format.
        Args:
            format_id: Format identifier (e.g., 'json', 'sql')
        Returns:
            Metadata dict or None if not found
        """
        return self._data.get(format_id)

    def detect_from_extension(self, file_path: str) -> str | None:
        """
        Detect format from file extension.
        Args:
            file_path: Path to file
        Returns:
            Format ID or None
        """
        ext = Path(file_path).suffix.lower()
        return self._extension_map.get(ext)

    def detect_from_mime_type(self, mime_type: str) -> str | None:
        """
        Detect format from MIME type.
        Args:
            mime_type: MIME type (e.g., 'application/json')
        Returns:
            Format ID or None
        """
        return self._mime_map.get(mime_type.lower())

    def detect_from_alias(self, alias: str) -> str | None:
        """
        Detect format from alias.
        Args:
            alias: Format alias or name
        Returns:
            Format ID or None
        """
        return self._alias_map.get(alias.lower())

    def find_format(self, identifier: str) -> str | None:
        """
        Find format by any identifier (ID, alias, extension, MIME type).
        Args:
            identifier: Format ID, alias, extension, or MIME type
        Returns:
            Format ID or None
        """
        # Try direct lookup
        if identifier in self._data:
            return identifier
        # Try alias lookup
        format_id = self._alias_map.get(identifier.lower())
        if format_id:
            return format_id
        # Try as extension
        if identifier.startswith('.'):
            format_id = self._extension_map.get(identifier.lower())
        else:
            format_id = self._extension_map.get(f'.{identifier.lower()}')
        if format_id:
            return format_id
        # Try as MIME type
        format_id = self._mime_map.get(identifier.lower())
        if format_id:
            return format_id
        return None

    def list_formats(self) -> list[str]:
        """
        List all format IDs.
        Returns:
            List of format IDs
        """
        return sorted(self._data.keys())

    def list_extensions(self) -> list[str]:
        """
        List all supported file extensions.
        Returns:
            List of extensions
        """
        return sorted(self._extension_map.keys())

    def list_mime_types(self) -> list[str]:
        """
        List all supported MIME types.
        Returns:
            List of MIME types
        """
        return sorted(self._mime_map.keys())

    def get_extensions(self, format_id: str) -> list[str]:
        """
        Get file extensions for a format.
        Args:
            format_id: Format identifier
        Returns:
            List of file extensions
        """
        metadata = self.get_metadata(format_id)
        if metadata:
            return metadata.get('file_extensions', [])
        return []

    def get_mime_types(self, format_id: str) -> list[str]:
        """
        Get MIME types for a format.
        Args:
            format_id: Format identifier
        Returns:
            List of MIME types
        """
        metadata = self.get_metadata(format_id)
        if metadata:
            return metadata.get('mime_types', [])
        return []

    def get_primary_mime_type(self, format_id: str) -> str | None:
        """
        Get primary MIME type for a format.
        Args:
            format_id: Format identifier
        Returns:
            Primary MIME type or None
        """
        metadata = self.get_metadata(format_id)
        if metadata:
            return metadata.get('primary_mime_type')
        return None

    def is_binary(self, format_id: str) -> bool:
        """
        Check if format is binary.
        Args:
            format_id: Format identifier
        Returns:
            True if binary format
        """
        metadata = self.get_metadata(format_id)
        if metadata:
            return metadata.get('is_binary', False)
        return False

    def supports_bidirectional(self, format_id: str) -> bool:
        """
        Check if format supports bidirectional conversion.
        Args:
            format_id: Format identifier
        Returns:
            True if bidirectional
        """
        metadata = self.get_metadata(format_id)
        if metadata:
            return metadata.get('supports_bidirectional', False)
        return False
# ============================================================================
# GLOBAL INSTANCE
# ============================================================================
_global_metadata: GrammarMetadata | None = None


def get_grammar_metadata() -> GrammarMetadata:
    """
    Get global grammar metadata instance.
    Returns:
        GrammarMetadata instance
    """
    global _global_metadata
    if _global_metadata is None:
        _global_metadata = GrammarMetadata()
    return _global_metadata
