"""
Setup API Endpoint Tests

Tests for the POST /api/setup endpoint that handles initial configuration
Tests setup completion, validation, redirect behavior, and persistence
"""

import pytest
from httpx import ASGITransport, AsyncClient

from src.server.fastapi_app import app
from src.server.services.auth_service import auth_service
from src.server.services.config_service import get_config_service


@pytest.fixture
async def client():
    """Create an async test client."""
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as ac:
        yield ac


@pytest.fixture
def unconfigured_auth():
    """Temporarily unconfigure auth so setup tests can run."""
    original_hash = auth_service._hash
    auth_service._hash = None
    yield
    auth_service._hash = original_hash


class TestSetupEndpoint:
    """Tests for the POST /api/setup endpoint."""

    async def test_setup_endpoint_exists(self, client):
        """Test that the setup endpoint responds."""
        # Prepare minimal valid setup data
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/test/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should not return 404
        assert response.status_code != 404

    async def test_setup_with_valid_data(self, client):
        """Test setup with all valid configuration data."""
        setup_data = {
            "master_password": "StrongPassword123!",
            "anime_directory": "/path/to/anime",
            "name": "Test Aniworld",
            "data_dir": "test_data",
            "scheduler_enabled": True,
            "scheduler_interval_minutes": 60,
            "logging_level": "INFO",
            "logging_file": "app.log",
            "logging_max_bytes": 10485760,
            "logging_backup_count": 5,
            "backup_enabled": True,
            "backup_path": "backups",
            "backup_keep_days": 30,
            "nfo_tmdb_api_key": "test_api_key_12345",
            "nfo_auto_create": True,
            "nfo_update_on_scan": False,
            "nfo_download_poster": True,
            "nfo_download_logo": True,
            "nfo_download_fanart": True,
            "nfo_image_size": "original"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should succeed (or return appropriate status if already configured)
        assert response.status_code in [201, 400]
        
        if response.status_code == 201:
            data = response.json()
            assert "message" in data or "status" in data

    async def test_setup_requires_master_password(self, client):
        """Test that setup requires a master password."""
        setup_data = {
            "anime_directory": "/test/anime"
            # Missing master_password
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should return validation error
        assert response.status_code == 422

    async def test_setup_with_weak_password(self, client):
        """Test that weak passwords are rejected."""
        setup_data = {
            "master_password": "weak",
            "anime_directory": "/test/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should return validation error or bad request
        assert response.status_code in [400, 422]

    async def test_setup_rejects_when_already_configured(self, client):
        """Test that setup endpoint rejects requests when already configured."""
        if not auth_service.is_configured():
            pytest.skip("Auth not configured, cannot test rejection")
        
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/test/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should return 400 Bad Request
        assert response.status_code == 400
        
        data = response.json()
        assert "already configured" in data["detail"].lower()

    async def test_setup_validates_scheduler_interval(self, client):
        """Test that invalid scheduler intervals are rejected."""
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/test/anime",
            "scheduler_interval_minutes": -10  # Invalid negative value
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should return validation error
        assert response.status_code == 422

    async def test_setup_validates_logging_level(self, client):
        """Test that invalid logging levels are rejected."""
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/test/anime",
            "logging_level": "INVALID_LEVEL"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should return validation error
        assert response.status_code in [400, 422]

    async def test_setup_with_optional_fields_only(self, client):
        """Test setup with only required fields."""
        setup_data = {
            "master_password": "MinimalPassword123!",
            "anime_directory": "/minimal/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should succeed or indicate already configured
        assert response.status_code in [201, 400]

    async def test_setup_saves_configuration(self, client, unconfigured_auth):
        """Test that setup persists configuration to config.json."""
        
        setup_data = {
            "master_password": "PersistentPassword123!",
            "anime_directory": "/persistent/anime",
            "name": "Persistent Test",
            "scheduler_enabled": False
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        if response.status_code == 201:
            # Verify config was saved
            config_service = get_config_service()
            config = config_service.load_config()
            
            assert config is not None
            assert config.name == "Persistent Test"
            assert config.scheduler.enabled == False


class TestSetupValidation:
    """Tests for setup endpoint validation logic."""

    async def test_password_minimum_length_validation(self, client):
        """Test that passwords shorter than 8 characters are rejected."""
        setup_data = {
            "master_password": "short",
            "anime_directory": "/test/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        assert response.status_code == 422
        data = response.json()
        assert any("password" in str(error).lower() for error in data.get("detail", []))

    async def test_anime_directory_required(self, client):
        """Test that anime directory is required."""
        setup_data = {
            "master_password": "ValidPassword123!"
            # Missing anime_directory
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # May require directory depending on implementation
        # At minimum should not crash
        assert response.status_code in [201, 400, 422]

    async def test_invalid_json_rejected(self, client):
        """Test that malformed JSON is rejected."""
        response = await client.post(
            "/api/auth/setup",
            content="invalid json {",
            headers={"Content-Type": "application/json"}
        )
        
        assert response.status_code == 422

    async def test_empty_request_rejected(self, client):
        """Test that empty request body is rejected."""
        response = await client.post("/api/auth/setup", json={})
        
        assert response.status_code == 422

    async def test_scheduler_interval_positive_validation(self, client):
        """Test that scheduler interval must be positive."""
        setup_data = {
            "master_password": "ValidPassword123!",
            "anime_directory": "/test/anime",
            "scheduler_interval_minutes": 0
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should reject zero or negative intervals
        assert response.status_code in [400, 422]

    async def test_backup_keep_days_validation(self, client):
        """Test that backup keep days is validated."""
        setup_data = {
            "master_password": "ValidPassword123!",
            "anime_directory": "/test/anime",
            "backup_keep_days": -5  # Invalid negative value
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        assert response.status_code == 422

    async def test_nfo_image_size_validation(self, client):
        """Test that NFO image size is validated."""
        setup_data = {
            "master_password": "ValidPassword123!",
            "anime_directory": "/test/anime",
            "nfo_image_size": "invalid_size"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should validate image size options
        assert response.status_code in [400, 422]


class TestSetupRedirect:
    """Tests for setup page redirect behavior."""

    async def test_redirect_to_setup_when_not_configured(self, client, unconfigured_auth):
        """Test that accessing root redirects to setup when not configured."""
        
        response = await client.get("/", follow_redirects=False)
        
        # Should redirect to setup
        if response.status_code in [301, 302, 303, 307, 308]:
            assert "/setup" in response.headers.get("location", "")

    async def test_setup_page_accessible_when_not_configured(self, client):
        """Test that setup page is accessible when not configured."""
        response = await client.get("/setup")
        
        # Should be accessible
        assert response.status_code in [200, 302]

    async def test_redirect_to_login_after_setup(self, client, unconfigured_auth):
        """Test that setup redirects to login/loading page after completion."""
        
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/test/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data, follow_redirects=False)
        
        if response.status_code == 201:
            # Check for redirect information in response
            data = response.json()
            # Response may contain redirect URL or loading page info
            assert "redirect" in data or "message" in data or "status" in data


class TestSetupPersistence:
    """Tests for setup configuration persistence."""

    async def test_setup_creates_config_file(self, client, unconfigured_auth):
        """Test that setup creates the configuration file."""
        
        setup_data = {
            "master_password": "PersistenceTest123!",
            "anime_directory": "/test/anime",
            "name": "Persistence Test"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        if response.status_code == 201:
            # Verify config file exists
            config_service = get_config_service()
            config = config_service.load_config()
            assert config is not None

    async def test_setup_persists_all_settings(self, client, unconfigured_auth):
        """Test that all provided settings are persisted."""
        
        setup_data = {
            "master_password": "CompleteTest123!",
            "anime_directory": "/complete/anime",
            "name": "Complete Setup",
            "scheduler_enabled": True,
            "scheduler_interval_minutes": 120,
            "backup_enabled": True,
            "nfo_auto_create": True
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        if response.status_code == 201:
            config_service = get_config_service()
            config = config_service.load_config()
            
            assert config.name == "Complete Setup"
            assert config.scheduler.enabled == True
            assert config.scheduler.interval_minutes == 120
            assert config.backup.enabled == True
            assert config.nfo.auto_create == True

    async def test_setup_stores_password_hash(self, client, unconfigured_auth):
        """Test that setup stores password hash, not plaintext."""
        
        password = "SecurePassword123!"
        setup_data = {
            "master_password": password,
            "anime_directory": "/secure/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        if response.status_code == 201:
            # Verify password is hashed
            config_service = get_config_service()
            config = config_service.load_config()
            
            stored_hash = config.other.get('master_password_hash', '')
            
            # Hash should not match plaintext password
            assert stored_hash != password
            # Hash should exist and be non-empty
            assert len(stored_hash) > 20


class TestSetupEdgeCases:
    """Tests for edge cases in setup endpoint."""

    async def test_setup_with_special_characters_in_paths(self, client):
        """Test that special characters in paths are handled."""
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/path/with spaces/and-dashes/and_underscores"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should handle special characters gracefully
        assert response.status_code in [201, 400, 422]

    async def test_setup_with_unicode_in_name(self, client):
        """Test that Unicode characters in name are handled."""
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/test/anime",
            "name": "アニメ Manager 日本語"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should handle Unicode gracefully
        assert response.status_code in [201, 400, 422]

    async def test_setup_with_very_long_values(self, client):
        """Test that very long input values are handled."""
        setup_data = {
            "master_password": "a" * 1000,  # Very long password
            "anime_directory": "/test/anime"
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should handle or reject gracefully
        assert response.status_code in [201, 400, 422]

    async def test_setup_with_null_values(self, client):
        """Test that null values are handled appropriately."""
        setup_data = {
            "master_password": "TestPassword123!",
            "anime_directory": "/test/anime",
            "name": None,
            "logging_level": None
        }
        
        response = await client.post("/api/auth/setup", json=setup_data)
        
        # Should handle null values (use defaults or reject)
        assert response.status_code in [201, 400, 422]
