"""
Unit-Tests für FileOperations (REST API).

Testet File-Upload und -Download mit gemockten HTTP-Responses
und Payload-Validierung gegen Swagger Schema.
"""

from __future__ import annotations

from datetime import datetime
from io import BytesIO
from pathlib import Path

import pytest

from questra_data.operations.rest_file import FileOperations
from tests.helpers.rest_validators import RestPayloadValidator


@pytest.mark.unit
class TestFileOperationsDownload:
    """Tests für download() Methode."""

    def test_download_basic(self, mock_rest_get, mock_rest_post):
        """Test download - erfolgreich."""
        # Setup Mock - binäre Datei simulieren
        file_content = b"PDF file content here"
        mock_rest_get.return_value = file_content

        # Test
        file_ops = FileOperations(mock_rest_get, mock_rest_post)
        result = file_ops.download(file_id=12345)

        # Assertions
        assert result == file_content
        assert isinstance(result, bytes)

        # REST GET wurde korrekt aufgerufen
        mock_rest_get.assert_called_once()
        call_args = mock_rest_get.call_args

        assert call_args[0][0] == "/file/12345"

    def test_download_empty_file(self, mock_rest_get, mock_rest_post):
        """Test download - leere Datei."""
        mock_rest_get.return_value = b""

        file_ops = FileOperations(mock_rest_get, mock_rest_post)
        result = file_ops.download(file_id=999)

        assert result == b""


@pytest.mark.unit
class TestFileOperationsUpload:
    """Tests für upload() Methode."""

    def test_upload_single_file_from_bytes(
        self, mock_rest_get, mock_rest_post, rest_file_upload_success
    ):
        """Test upload - einzelne Datei aus BytesIO."""
        mock_rest_post.return_value = rest_file_upload_success

        file_ops = FileOperations(mock_rest_get, mock_rest_post)

        file_data = BytesIO(b"PDF content here")
        files = [
            ("MyInventory.DocumentProperty", file_data, "document.pdf", "application/pdf")
        ]

        result = file_ops.upload(files)

        # Assertions
        assert len(result) == 1
        file_obj = result[0]

        assert file_obj.id == 12345
        assert file_obj.name == "document.pdf"
        assert file_obj.media_type == "application/pdf"
        assert file_obj.size == 102400
        assert file_obj.inventory_property_id == 789
        assert file_obj.audit_enabled is True
        assert file_obj.deleted_at is None

        # Timestamps validieren
        assert isinstance(file_obj.created_at, datetime)

        # REST POST wurde korrekt aufgerufen
        mock_rest_post.assert_called_once()
        call_args = mock_rest_post.call_args

        assert call_args[0][0] == "/file/upload"
        assert "files" in call_args[1]

        files_dict = call_args[1]["files"]
        assert "MyInventory.DocumentProperty" in files_dict

        # Payload validieren
        validation = RestPayloadValidator.validate_file_upload_response(
            mock_rest_post.return_value
        )
        assert validation["valid"] is True, (
            f"Payload-Validierung fehlgeschlagen: {validation['errors']}"
        )

    def test_upload_multiple_files(
        self, mock_rest_get, mock_rest_post, rest_file_upload_multiple_success
    ):
        """Test upload - mehrere Dateien."""
        mock_rest_post.return_value = rest_file_upload_multiple_success

        file_ops = FileOperations(mock_rest_get, mock_rest_post)

        file1 = BytesIO(b"PDF content")
        file2 = BytesIO(b"JPEG image data")

        files = [
            ("TestInventory.DocProperty", file1, "document1.pdf", "application/pdf"),
            ("TestInventory.ImageProperty", file2, "image.jpg", "image/jpeg"),
        ]

        result = file_ops.upload(files)

        # Assertions
        assert len(result) == 2

        # Erste Datei
        assert result[0].id == 12345
        assert result[0].name == "document1.pdf"
        assert result[0].media_type == "application/pdf"
        assert result[0].inventory_property_id == 789
        assert result[0].audit_enabled is True

        # Zweite Datei
        assert result[1].id == 12346
        assert result[1].name == "image.jpg"
        assert result[1].media_type == "image/jpeg"
        assert result[1].inventory_property_id == 790
        assert result[1].audit_enabled is False

        # Payload validieren
        validation = RestPayloadValidator.validate_file_upload_response(
            mock_rest_post.return_value
        )
        assert validation["valid"] is True

    def test_upload_with_namespace(
        self, mock_rest_get, mock_rest_post, rest_file_upload_success
    ):
        """Test upload - mit Namespace-Angabe."""
        mock_rest_post.return_value = rest_file_upload_success

        file_ops = FileOperations(mock_rest_get, mock_rest_post)

        file_data = BytesIO(b"Content")
        files = [
            (
                "MyNamespace.MyInventory.DocumentProperty",
                file_data,
                "doc.pdf",
                "application/pdf",
            )
        ]

        result = file_ops.upload(files)

        assert len(result) == 1

        # Prüfe POST Call
        call_args = mock_rest_post.call_args
        files_dict = call_args[1]["files"]
        assert "MyNamespace.MyInventory.DocumentProperty" in files_dict

    def test_upload_single_convenience_method(
        self, mock_rest_get, mock_rest_post, rest_file_upload_success
    ):
        """Test upload_single - Convenience-Methode."""
        mock_rest_post.return_value = rest_file_upload_success

        file_ops = FileOperations(mock_rest_get, mock_rest_post)

        file_data = BytesIO(b"PDF content")

        result = file_ops.upload_single(
            name="MyInventory.DocumentProperty",
            file_data=file_data,
            filename="document.pdf",
            content_type="application/pdf",
        )

        # Assertions
        assert result.id == 12345
        assert result.name == "document.pdf"

        # upload() wurde intern aufgerufen
        mock_rest_post.assert_called_once()


@pytest.mark.unit
class TestFileOperationsWithFilePaths:
    """Tests für upload() mit echten Dateipfaden (ohne I/O)."""

    def test_upload_file_path_structure(
        self, mock_rest_get, mock_rest_post, rest_file_upload_success, tmp_path
    ):
        """Test upload - Dateipfad wird korrekt verarbeitet."""
        mock_rest_post.return_value = rest_file_upload_success

        # Temporäre Datei erstellen
        temp_file = tmp_path / "test_document.pdf"
        temp_file.write_bytes(b"Test PDF content")

        file_ops = FileOperations(mock_rest_get, mock_rest_post)

        files = [
            ("MyInventory.DocumentProperty", str(temp_file), "test_document.pdf", "application/pdf")
        ]

        result = file_ops.upload(files)

        # Assertions
        assert len(result) == 1

        # Prüfe dass POST aufgerufen wurde
        mock_rest_post.assert_called_once()
        call_args = mock_rest_post.call_args

        assert call_args[0][0] == "/file/upload"
        assert "files" in call_args[1]

    def test_upload_path_object(
        self, mock_rest_get, mock_rest_post, rest_file_upload_success, tmp_path
    ):
        """Test upload - mit Path-Objekt."""
        mock_rest_post.return_value = rest_file_upload_success

        # Temporäre Datei erstellen
        temp_file = tmp_path / "image.jpg"
        temp_file.write_bytes(b"JPEG data")

        file_ops = FileOperations(mock_rest_get, mock_rest_post)

        files = [
            ("MyInventory.ImageProperty", temp_file, "image.jpg", "image/jpeg")
        ]

        result = file_ops.upload(files)

        assert len(result) == 1
        assert result[0].id == 12345
