
from fnmatch import fnmatch
import re
from pathlib import Path
from typing import Any, Dict, List, Optional
from ontobdc.module.resource.adapter.renderer.document_list import DocumentListRenderer
from ontobdc.module.resource.audit.repository import HasReadPermission
from ontobdc.module.resource.domain.port.repository import DocumentRepositoryPort
from ontobdc.run.core.capability import Capability, CapabilityMetadata
from ontobdc.run.core.port.contex import CliContextPort


class ListDocumentsByTypeCapability(Capability):
    """
    Capability to list documents in a repository filtered by type.
    """
    METADATA = CapabilityMetadata(
        id="org.ontobdc.domain.resource.capability.list_documents_by_type",
        version="0.1.0",
        name="List Documents by Type",
        description="Lists documents from a DocumentRepositoryPort filtered by type.",
        author=["Elias M. P. Junior"],
        tags=["resource", "document", "listing", "type"],
        supported_languages=["en", "pt_BR"],
        input_schema={
            "type": "object",
            "properties": {
                "repository": {
                    "type": DocumentRepositoryPort,
                    "uri": "org.ontobdc.domain.resource.document.repository.incoming",
                    "required": True,
                    "description": "Repository instance (DocumentRepositoryPort)",
                    "check": [HasReadPermission]
                },
                "file_type": {
                    "type": "array",
                    "uri": "org.ontobdc.domain.resource.input.file.type",
                    "required": True,
                    "description": "List of document types to filter by (e.g., ['pdf', 'json'])",
                },
                "file_name": {
                    "type": "string",
                    "uri": "org.ontobdc.domain.resource.input.file.name",
                    "required": False,
                    "description": "Optional name pattern to filter by (glob or regex:)",
                },
                "start": {
                    "type": "integer",
                    "uri": "org.ontobdc.domain.resource.input.pagination.start",
                    "required": False,
                    "description": "Starting index for pagination (0 = first)",
                },
                "limit": {
                    "type": "integer",
                    "uri": "org.ontobdc.domain.resource.input.pagination.limit",
                    "required": False,
                    "description": "Maximum number of documents to return (0 = no limit)",
                },
            },
        },
        output_schema={
            "type": "object",
            "properties": {
                "org.ontobdc.domain.resource.document.list.content": {
                    "type": "array",
                    "description": "List of document paths",
                },
                "org.ontobdc.domain.resource.document.list.count": {
                    "type": "integer",
                    "description": "Number of documents listed",
                },
            },
        },
        raises=[
            {
                "code": "org.ontobdc.domain.resource.document.exception.repository_not_configured",
                "python_type": "ValueError",
                "description": "File repository not configured for capability",
            },
            {
                "code": "org.ontobdc.domain.resource.document.exception.invalid_type_filter",
                "python_type": "ValueError",
                "description": "Invalid or empty type filter provided",
            },
        ],
    )

    def get_default_cli_renderer(self) -> Optional[Any]:
        return DocumentListRenderer()

    def execute(self, context: CliContextPort) -> Dict[str, Any]:
        types: List[str] = context.get_parameter_value("file_type")
            
        limit: int = context.get_parameter_value("limit", 0)
        start: int = context.get_parameter_value("start", 0)
        repository: DocumentRepositoryPort = context.get_parameter_value("repository")

        if not types:
            raise ValueError("Types must be a non-empty list of strings")

        documents: List[Any] = []
        for t in types:
            docs = repository.get_by_type(t)
            if isinstance(docs, list):
                documents.extend(docs)

        # Apply name pattern filtering if provided
        name_pattern: Optional[str] = context.get_parameter_value("file_name")
        if name_pattern:
            is_regex = False
            regex = None
            if name_pattern.startswith("regex:"):
                is_regex = True
                try:
                    regex = re.compile(name_pattern[6:])
                except re.error as e:
                    raise ValueError(f"Invalid regex pattern: {e}")

            filtered_by_name: List[Any] = []
            for doc in documents:
                name = self._extract_name(doc)
                if not name:
                    continue
                
                # Extract filename from path if needed
                filename = Path(name).name
                
                if is_regex:
                    if regex and regex.match(filename):
                        filtered_by_name.append(doc)
                else:
                    if fnmatch(filename, name_pattern):
                        filtered_by_name.append(doc)
            documents = filtered_by_name

        if start > 0:
            documents = documents[start:]

        if limit > 0:
            documents = documents[:limit]

        return {
            "org.ontobdc.domain.resource.document.list.content": documents,
            "org.ontobdc.domain.resource.document.list.count": len(documents),
        }

    def _extract_name(self, doc: Any) -> Optional[str]:
        if isinstance(doc, dict):
            for key in ("name", "filename", "file_name", "path", "filepath", "file"):
                value = doc.get(key)
                if isinstance(value, str):
                    return value
            return None
        if isinstance(doc, str):
            return doc
        return None
