"""
Integration tests for WebDAV upload with real server.

These tests spin up a real WebDAV server in a Docker container and test
actual file uploads against it, then verify the files were uploaded successfully.
"""

import os
import time
from unittest.mock import patch

import pytest
import requests

from app.tasks.upload_to_webdav import upload_to_webdav

# Import testcontainers - required for these tests
pytest.importorskip("testcontainers", reason="testcontainers not installed")
from testcontainers.core.container import DockerContainer

_TEST_CREDENTIAL = "testpass"  # noqa: S105
_TEST_WRONG_CREDENTIAL = "wrongpass"  # noqa: S105


@pytest.mark.integration
@pytest.mark.requires_docker
class TestWebDAVIntegration:
    """Integration tests with real WebDAV server."""

    @pytest.fixture(scope="class")
    def webdav_server(self):
        """
        Start a real WebDAV server in a Docker container.

        Uses bytemark/webdav image which provides a simple WebDAV server.
        """
        # Start WebDAV container
        container = DockerContainer("bytemark/webdav:latest")
        container.with_exposed_ports(80)
        container.with_env("AUTH_TYPE", "Basic")
        container.with_env("USERNAME", "testuser")
        container.with_env("PASSWORD", _TEST_CREDENTIAL)

        # Start the container
        container.start()

        # Wait for server to be ready
        time.sleep(2)

        # Get the mapped port
        host = container.get_container_host_ip()
        port = container.get_exposed_port(80)

        server_info = {
            "container": container,
            "host": host,
            "port": port,
            "url": f"http://{host}:{port}",
            "username": "testuser",
            "password": _TEST_CREDENTIAL,
        }

        # Verify server is accessible
        try:
            response = requests.get(
                server_info["url"], auth=(server_info["username"], server_info["password"]), timeout=5
            )
            assert response.status_code in [
                200,
                301,
                302,
                401,
            ], f"WebDAV server not ready, got status {response.status_code}"
        except Exception as e:
            container.stop()
            pytest.fail(f"Failed to connect to WebDAV server: {e}")

        yield server_info

        # Cleanup
        container.stop()

    def test_upload_file_to_real_webdav_server(self, webdav_server, sample_text_file):
        """Test uploading a file to a real WebDAV server."""
        with (
            patch("app.tasks.upload_to_webdav.settings") as mock_settings,
            patch("app.tasks.upload_to_webdav.log_task_progress"),
        ):
            # Configure settings to point to real WebDAV server
            mock_settings.webdav_url = webdav_server["url"] + "/"
            mock_settings.webdav_username = webdav_server["username"]
            mock_settings.webdav_password = webdav_server["password"]
            mock_settings.webdav_folder = ""
            mock_settings.webdav_verify_ssl = False  # HTTP server
            mock_settings.http_request_timeout = 30

            # Upload the file
            result = upload_to_webdav.apply(args=[sample_text_file], kwargs={"file_id": 1}).get()

            # Verify upload succeeded
            assert result["status"] == "Completed"
            assert result["file"] == sample_text_file
            assert "url" in result

            # Verify the file actually exists on the server
            filename = os.path.basename(sample_text_file)
            file_url = f"{webdav_server['url']}/{filename}"

            response = requests.get(file_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=5)

            assert response.status_code == 200, f"File not found on server: {response.status_code}"

            # Verify file content matches
            with open(sample_text_file, "rb") as f:
                expected_content = f.read()

            assert response.content == expected_content, "Uploaded file content does not match original"

    def test_upload_to_subfolder(self, webdav_server, sample_text_file):
        """Test uploading a file to a subfolder on WebDAV server."""
        with (
            patch("app.tasks.upload_to_webdav.settings") as mock_settings,
            patch("app.tasks.upload_to_webdav.log_task_progress"),
        ):
            # Create a test folder first
            folder_name = "test-uploads"
            folder_url = f"{webdav_server['url']}/{folder_name}"

            # Create folder using MKCOL method
            requests.request(
                "MKCOL", folder_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=5
            )

            # Configure settings
            mock_settings.webdav_url = webdav_server["url"] + "/"
            mock_settings.webdav_username = webdav_server["username"]
            mock_settings.webdav_password = webdav_server["password"]
            mock_settings.webdav_folder = folder_name
            mock_settings.webdav_verify_ssl = False
            mock_settings.http_request_timeout = 30

            # Upload the file
            result = upload_to_webdav.apply(args=[sample_text_file], kwargs={"file_id": 2}).get()

            # Verify upload succeeded
            assert result["status"] == "Completed"

            # Verify the file exists in the subfolder
            filename = os.path.basename(sample_text_file)
            file_url = f"{webdav_server['url']}/{folder_name}/{filename}"

            response = requests.get(file_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=5)

            assert response.status_code == 200, f"File not found in subfolder: {response.status_code}"

    def test_upload_pdf_file(self, webdav_server, sample_pdf_path):
        """Test uploading a PDF file to WebDAV server."""
        with (
            patch("app.tasks.upload_to_webdav.settings") as mock_settings,
            patch("app.tasks.upload_to_webdav.log_task_progress"),
        ):
            mock_settings.webdav_url = webdav_server["url"] + "/"
            mock_settings.webdav_username = webdav_server["username"]
            mock_settings.webdav_password = webdav_server["password"]
            mock_settings.webdav_folder = ""
            mock_settings.webdav_verify_ssl = False
            mock_settings.http_request_timeout = 30

            # Upload the PDF
            result = upload_to_webdav.apply(args=[sample_pdf_path], kwargs={"file_id": 3}).get()

            # Verify upload succeeded
            assert result["status"] == "Completed"

            # Verify the file exists
            filename = os.path.basename(sample_pdf_path)
            file_url = f"{webdav_server['url']}/{filename}"

            response = requests.get(file_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=5)

            assert response.status_code == 200

            # Verify it's a PDF (check magic bytes)
            assert response.content.startswith(b"%PDF"), "Uploaded file is not a valid PDF"

    def test_upload_with_wrong_credentials(self, webdav_server, sample_text_file):
        """Test that upload fails with wrong credentials."""
        with (
            patch("app.tasks.upload_to_webdav.settings") as mock_settings,
            patch("app.tasks.upload_to_webdav.log_task_progress"),
        ):
            mock_settings.webdav_url = webdav_server["url"] + "/"
            mock_settings.webdav_username = "wronguser"
            mock_settings.webdav_password = _TEST_WRONG_CREDENTIAL
            mock_settings.webdav_folder = ""
            mock_settings.webdav_verify_ssl = False
            mock_settings.http_request_timeout = 30

            # Upload should fail with 401 Unauthorized
            with pytest.raises(Exception, match="Failed to upload.*401"):
                upload_to_webdav.apply(args=[sample_text_file], kwargs={"file_id": 4}).get()

    def test_upload_multiple_files(self, webdav_server, sample_text_file, tmp_path):
        """Test uploading multiple files sequentially."""
        with (
            patch("app.tasks.upload_to_webdav.settings") as mock_settings,
            patch("app.tasks.upload_to_webdav.log_task_progress"),
        ):
            mock_settings.webdav_url = webdav_server["url"] + "/"
            mock_settings.webdav_username = webdav_server["username"]
            mock_settings.webdav_password = webdav_server["password"]
            mock_settings.webdav_folder = ""
            mock_settings.webdav_verify_ssl = False
            mock_settings.http_request_timeout = 30

            # Create additional test files
            file1 = tmp_path / "test1.txt"
            file1.write_text("Test file 1")

            file2 = tmp_path / "test2.txt"
            file2.write_text("Test file 2")

            file3 = tmp_path / "test3.txt"
            file3.write_text("Test file 3")

            # Upload all files
            files = [str(file1), str(file2), str(file3)]
            uploaded_files = []

            for idx, file_path in enumerate(files, start=1):
                result = upload_to_webdav.apply(args=[file_path], kwargs={"file_id": idx + 10}).get()

                assert result["status"] == "Completed"
                uploaded_files.append(os.path.basename(file_path))

            # Verify all files exist on server
            for filename in uploaded_files:
                file_url = f"{webdav_server['url']}/{filename}"
                response = requests.get(
                    file_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=5
                )
                assert response.status_code == 200, f"File {filename} not found on server"

    def test_overwrite_existing_file(self, webdav_server, sample_text_file, tmp_path):
        """Test that uploading a file with the same name overwrites the existing one."""
        with (
            patch("app.tasks.upload_to_webdav.settings") as mock_settings,
            patch("app.tasks.upload_to_webdav.log_task_progress"),
        ):
            mock_settings.webdav_url = webdav_server["url"] + "/"
            mock_settings.webdav_username = webdav_server["username"]
            mock_settings.webdav_password = webdav_server["password"]
            mock_settings.webdav_folder = ""
            mock_settings.webdav_verify_ssl = False
            mock_settings.http_request_timeout = 30

            # Create two files with same name but different content
            file1 = tmp_path / "duplicate.txt"
            file1.write_text("Original content")

            # Upload first version
            result1 = upload_to_webdav.apply(args=[str(file1)], kwargs={"file_id": 20}).get()
            assert result1["status"] == "Completed"

            # Verify first version
            file_url = f"{webdav_server['url']}/duplicate.txt"
            response = requests.get(file_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=5)
            assert response.text == "Original content"

            # Update file content
            file1.write_text("Updated content - version 2")

            # Upload second version
            result2 = upload_to_webdav.apply(args=[str(file1)], kwargs={"file_id": 21}).get()
            assert result2["status"] == "Completed"

            # Verify file was overwritten
            response = requests.get(file_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=5)
            assert response.text == "Updated content - version 2"

    def test_large_file_upload(self, webdav_server, tmp_path):
        """Test uploading a larger file (1MB)."""
        with (
            patch("app.tasks.upload_to_webdav.settings") as mock_settings,
            patch("app.tasks.upload_to_webdav.log_task_progress"),
        ):
            mock_settings.webdav_url = webdav_server["url"] + "/"
            mock_settings.webdav_username = webdav_server["username"]
            mock_settings.webdav_password = webdav_server["password"]
            mock_settings.webdav_folder = ""
            mock_settings.webdav_verify_ssl = False
            mock_settings.http_request_timeout = 60  # Longer timeout for large file

            # Create a 1MB test file
            large_file = tmp_path / "large_file.bin"
            large_file.write_bytes(b"X" * (1024 * 1024))  # 1MB of X's

            # Upload the large file
            result = upload_to_webdav.apply(args=[str(large_file)], kwargs={"file_id": 30}).get()

            assert result["status"] == "Completed"

            # Verify file exists and has correct size
            file_url = f"{webdav_server['url']}/large_file.bin"
            response = requests.get(file_url, auth=(webdav_server["username"], webdav_server["password"]), timeout=10)

            assert response.status_code == 200
            assert len(response.content) == 1024 * 1024, (
                f"File size mismatch: expected 1MB, got {len(response.content)} bytes"
            )


@pytest.mark.integration
@pytest.mark.requires_docker
class TestWebDAVServerVerification:
    """Tests to verify WebDAV server behavior."""

    @pytest.fixture(scope="class")
    def webdav_server(self):
        """Start WebDAV server for verification tests."""
        container = DockerContainer("bytemark/webdav:latest")
        container.with_exposed_ports(80)
        container.with_env("AUTH_TYPE", "Basic")
        container.with_env("USERNAME", "admin")
        container.with_env("PASSWORD", "admin123")

        container.start()
        time.sleep(2)

        host = container.get_container_host_ip()
        port = container.get_exposed_port(80)

        server_info = {
            "container": container,
            "url": f"http://{host}:{port}",
            "username": "admin",
            "password": "admin123",
        }

        yield server_info
        container.stop()

    def test_webdav_server_basic_auth(self, webdav_server):
        """Verify WebDAV server requires authentication."""
        # Request without auth should fail
        response = requests.get(webdav_server["url"], timeout=5)
        assert response.status_code == 401

        # Request with auth should succeed
        response = requests.get(
            webdav_server["url"], auth=(webdav_server["username"], webdav_server["password"]), timeout=5
        )
        assert response.status_code in [200, 301, 302]

    def test_webdav_put_method(self, webdav_server, tmp_path):
        """Verify WebDAV server accepts PUT requests."""
        test_file = tmp_path / "put_test.txt"
        test_file.write_text("PUT method test")

        with open(test_file, "rb") as f:
            response = requests.put(
                f"{webdav_server['url']}/put_test.txt",
                auth=(webdav_server["username"], webdav_server["password"]),
                data=f,
                timeout=5,
            )

        assert response.status_code in [200, 201, 204]

    def test_webdav_propfind_method(self, webdav_server):
        """Verify WebDAV server supports PROPFIND (directory listing)."""
        response = requests.request(
            "PROPFIND", webdav_server["url"], auth=(webdav_server["username"], webdav_server["password"]), timeout=5
        )

        # PROPFIND may return 207 Multi-Status, 200 OK, or 403 Forbidden
        # depending on server configuration
        assert response.status_code in [200, 207, 403]
