"""REST Operations für File-Endpoints."""

from __future__ import annotations

from collections.abc import Callable
from datetime import datetime
from pathlib import Path
from typing import BinaryIO

from loguru import logger

from ..models.rest import File


class FileOperations:
    """
    REST-Operationen für File-Endpoints.

    Bietet Methoden zum Hochladen und Herunterladen von Dateien.
    """

    def __init__(self, rest_get_func: Callable, rest_post_func: Callable):
        """
        Initialisiert die File-Operationen.

        Args:
            rest_get_func: Funktion für GET-Requests
            rest_post_func: Funktion für POST-Requests
        """
        self._get = rest_get_func
        self._post = rest_post_func
        logger.debug("FileOperations initialized")

    def download(self, file_id: int) -> bytes:
        """
        Lädt eine Datei herunter.

        Args:
            file_id: ID der Datei

        Returns:
            bytes: Dateiinhalt als Bytes
        """
        logger.info(f"Downloading file with ID {file_id}")
        result = self._get(f"/file/{file_id}")
        logger.info(f"File downloaded successfully, size: {len(result)} bytes")
        return result

    def upload(
        self,
        files: list[tuple[str, str | Path | BinaryIO, str, str]],
    ) -> list[File]:
        """
        Lädt eine oder mehrere Dateien hoch.

        Args:
            files: Liste von Tupeln (name, file_data, filename, content_type)
                - name: Name im Format "Namespace.Inventory.Property" oder "Inventory.Property"
                - file_data: Dateipfad (str/Path) oder File-Objekt (BinaryIO)
                - filename: Originaler Dateiname (z.B. 'document.pdf')
                - content_type: MIME-Type (z.B. 'application/pdf')

        Returns:
            list[File]: Liste von File-Objekten mit Metadaten

        Examples:
            ```python
            # Upload mit Dateipfad
            result = client.files.upload(
                [
                    (
                        "MyInventory.DocumentProperty",
                        "/path/to/file.pdf",
                        "file.pdf",
                        "application/pdf",
                    )
                ]
            )

            # Upload mit File-Objekt
            with open("file.pdf", "rb") as f:
                result = client.files.upload(
                    [("MyInventory.DocumentProperty", f, "file.pdf", "application/pdf")]
                )
            ```
        """
        logger.info(f"Uploading {len(files)} file(s)")

        # Konvertiere Files zu requests-Format
        files_dict = {}
        opened_files = []

        try:
            for idx, (name, file_data, filename, content_type) in enumerate(files):
                # Wenn file_data ein Pfad ist, öffne die Datei
                if isinstance(file_data, (str, Path)):
                    file_obj = open(file_data, "rb")
                    opened_files.append(file_obj)
                else:
                    file_obj = file_data

                # Format: (filename, file_object, content_type)
                files_dict[name] = (filename, file_obj, content_type)

            # POST mit multipart/form-data
            result = self._post("/file/upload", files=files_dict)

            # Parse Response
            file_objects = []
            for file_data in result:
                file_obj = File(
                    id=file_data["id"],
                    created_by=file_data["createdBy"],
                    created_at=datetime.fromisoformat(
                        file_data["createdAt"].replace("Z", "+00:00")
                    ),
                    name=file_data.get("name"),
                    media_type=file_data.get("mediaType"),
                    charset=file_data.get("charset"),
                    size=file_data["size"],
                    hash_algorithm=file_data.get("hashAlgorithm"),
                    hash_base64=file_data.get("hashBase64"),
                    inventory_property_id=file_data["inventoryPropertyId"],
                    inventory_item_id=file_data.get("inventoryItemId"),
                    deleted_at=(
                        datetime.fromisoformat(
                            file_data["deletedAt"].replace("Z", "+00:00")
                        )
                        if file_data.get("deletedAt")
                        else None
                    ),
                    storage_delete_attempts=file_data["storageDeleteAttempts"],
                    audit_enabled=file_data["auditEnabled"],
                )
                file_objects.append(file_obj)

            logger.info(f"Successfully uploaded {len(file_objects)} file(s)")
            return file_objects

        finally:
            # Schließe alle geöffneten Dateien
            for file_obj in opened_files:
                try:
                    file_obj.close()
                except Exception as e:
                    logger.warning(f"Failed to close file: {e}")

    def upload_single(
        self,
        name: str,
        file_data: str | Path | BinaryIO,
        filename: str,
        content_type: str,
    ) -> File:
        """
        Lädt eine einzelne Datei hoch (Convenience-Methode).

        Args:
            name: Name im Format "Namespace.Inventory.Property" oder "Inventory.Property"
            file_data: Dateipfad (str/Path) oder File-Objekt (BinaryIO)
            filename: Originaler Dateiname (z.B. 'document.pdf')
            content_type: MIME-Type (z.B. 'application/pdf')

        Returns:
            File: Objekt mit Metadaten

        Examples:
            ```python
            result = client.files.upload_single(
                name="MyInventory.DocumentProperty",
                file_data="/path/to/file.pdf",
                filename="file.pdf",
                content_type="application/pdf",
            )
            ```
        """
        result = self.upload([(name, file_data, filename, content_type)])
        return result[0]
