"""
Tests for LOCAL_MODE behavior.

Verifies:
1. Multi-tenant mode (LOCAL_MODE=false) is unchanged
2. Local mode (LOCAL_MODE=true) bootstrap works correctly
3. Meta endpoint is stable in all scenarios
"""

import pytest
from unittest.mock import patch, AsyncMock
from uuid import uuid4

from fastapi.testclient import TestClient
from sqlalchemy import select

from app.config import Settings
from app.main import app
from app.models import Tenant
from app.services.local_bootstrap import LocalBootstrapService


class TestMultiTenantModeUnchanged:
    """
    Tests that multi-tenant mode (LOCAL_MODE=false, the default) remains unchanged.

    Key assertions:
    - No automatic tenant creation on startup
    - Tenants must be created manually via API
    - Bootstrap service does nothing
    """

    def test_local_mode_default_is_false(self):
        """Verify LOCAL_MODE defaults to False."""
        settings = Settings()
        assert settings.LOCAL_MODE is False

    def test_bootstrap_service_skips_when_local_mode_false(self):
        """Verify bootstrap does nothing when LOCAL_MODE=false."""
        # Reset class state
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = False
        LocalBootstrapService._bootstrap_error = None

        # Patch settings in the bootstrap module where it's used
        with patch('app.services.local_bootstrap.settings') as mock_settings:
            mock_settings.LOCAL_MODE = False

            import asyncio
            asyncio.get_event_loop().run_until_complete(LocalBootstrapService.run())

        # Should not have attempted anything
        assert LocalBootstrapService._bootstrap_attempted is False
        assert LocalBootstrapService._local_tenant_id is None

    def test_meta_endpoint_in_multi_tenant_mode(self):
        """Verify meta endpoint returns correct values in multi-tenant mode."""
        # Reset bootstrap state
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = False
        LocalBootstrapService._bootstrap_error = None

        with patch('app.config.settings') as mock_settings:
            mock_settings.LOCAL_MODE = False
            mock_settings.INSTANCE_ID = None
            mock_settings.APP_VERSION = "0.1.0"

            client = TestClient(app)
            response = client.get("/api/v1/meta/instance")

            assert response.status_code == 200
            data = response.json()
            assert data["local_mode"] is False
            assert data["tenant_id"] is None
            # instance_id may or may not be set depending on config


class TestLocalModeBootstrap:
    """
    Tests for LOCAL_MODE=true bootstrap behavior.
    """

    def test_bootstrap_requires_instance_id(self):
        """Verify bootstrap fails gracefully without INSTANCE_ID."""
        # Reset class state
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = False
        LocalBootstrapService._bootstrap_error = None

        with patch('app.services.local_bootstrap.settings') as mock_settings:
            mock_settings.LOCAL_MODE = True
            mock_settings.INSTANCE_ID = None

            import asyncio
            asyncio.get_event_loop().run_until_complete(LocalBootstrapService.run())

        assert LocalBootstrapService._bootstrap_attempted is True
        assert LocalBootstrapService._local_tenant_id is None
        assert "INSTANCE_ID is required" in LocalBootstrapService._bootstrap_error

    def test_bootstrap_status_tracking(self):
        """Verify bootstrap status is correctly tracked."""
        # Reset class state
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = False
        LocalBootstrapService._bootstrap_error = None

        status = LocalBootstrapService.get_bootstrap_status()
        assert status["attempted"] is False
        assert status["success"] is False
        assert status["tenant_id"] is None
        assert status["error"] is None


class TestMetaEndpointStability:
    """
    Tests that /api/v1/meta/instance is stable in all scenarios.

    Key assertions:
    - Endpoint works before tenant exists
    - Endpoint works after tenant bootstrap fails
    - Endpoint always returns required fields
    """

    def test_meta_endpoint_returns_required_fields(self):
        """Verify meta endpoint always returns required fields."""
        # Reset bootstrap state
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = False
        LocalBootstrapService._bootstrap_error = None

        client = TestClient(app)
        response = client.get("/api/v1/meta/instance")

        assert response.status_code == 200
        data = response.json()

        # These fields must always be present
        assert "local_mode" in data
        assert "version" in data
        assert "bootstrap_status" in data

        # These fields can be null but must be present
        assert "instance_id" in data
        assert "tenant_id" in data

    def test_meta_endpoint_after_failed_bootstrap(self):
        """Verify meta endpoint works even after bootstrap failure."""
        # Simulate failed bootstrap
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = True
        LocalBootstrapService._bootstrap_error = "Test error"

        client = TestClient(app)
        response = client.get("/api/v1/meta/instance")

        assert response.status_code == 200
        data = response.json()

        # Should still return all fields
        assert data["bootstrap_status"]["attempted"] is True
        assert data["bootstrap_status"]["success"] is False
        assert data["bootstrap_status"]["error"] == "Test error"

    def test_meta_endpoint_with_successful_bootstrap(self):
        """Verify meta endpoint reflects successful bootstrap."""
        tenant_id = uuid4()

        # Simulate successful bootstrap
        LocalBootstrapService._local_tenant_id = tenant_id
        LocalBootstrapService._bootstrap_attempted = True
        LocalBootstrapService._bootstrap_error = None

        with patch('app.routes.meta.settings') as mock_settings:
            mock_settings.LOCAL_MODE = True
            mock_settings.INSTANCE_ID = "test-instance-123"
            mock_settings.APP_VERSION = "0.1.0"

            client = TestClient(app)
            response = client.get("/api/v1/meta/instance")

            assert response.status_code == 200
            data = response.json()

            assert data["local_mode"] is True
            assert data["instance_id"] == "test-instance-123"
            assert data["tenant_id"] == str(tenant_id)
            assert data["bootstrap_status"]["success"] is True


class TestConfigDefaults:
    """
    Tests for configuration defaults related to LOCAL_MODE.
    """

    def test_all_local_mode_settings_have_safe_defaults(self):
        """Verify all LOCAL_MODE settings have safe defaults that don't break multi-tenant mode."""
        settings = Settings()

        # Core setting defaults to False (multi-tenant)
        assert settings.LOCAL_MODE is False

        # Optional settings default to None/False
        assert settings.INSTANCE_ID is None
        assert settings.HUB_URL is None
        assert settings.HUB_API_KEY is None
        assert settings.HUB_TELEMETRY_ENABLED is False

        # Local tenant domain has a default
        assert settings.LOCAL_TENANT_DOMAIN == "local.letsbe.cloud"

        # Phase 2: LOCAL_AGENT_KEY defaults to None
        assert settings.LOCAL_AGENT_KEY is None


class TestLocalAgentRegistration:
    """
    Tests for /api/v1/agents/register-local endpoint (Phase 2).

    HTTP Status Semantics:
    - 404: Endpoint hidden when LOCAL_MODE=false
    - 401: Invalid or missing LOCAL_AGENT_KEY
    - 503: Local tenant not bootstrapped
    - 201: New agent created
    - 200: Existing agent returned (idempotent)
    """

    def test_register_local_hidden_when_local_mode_false(self):
        """Verify /register-local returns 404 when LOCAL_MODE=false (security by obscurity)."""
        # Reset bootstrap state
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = False
        LocalBootstrapService._bootstrap_error = None

        with patch('app.dependencies.local_agent_auth.get_settings') as mock_get_settings:
            mock_settings = Settings()
            mock_settings.LOCAL_MODE = False
            mock_get_settings.return_value = mock_settings

            client = TestClient(app)
            response = client.post(
                "/api/v1/agents/register-local",
                json={"hostname": "test-agent", "version": "1.0.0"},
                headers={"X-Local-Agent-Key": "any-key"},
            )

            assert response.status_code == 404

    def test_register_local_requires_local_agent_key(self):
        """Verify /register-local returns 401 without X-Local-Agent-Key header."""
        # Reset bootstrap state
        LocalBootstrapService._local_tenant_id = uuid4()
        LocalBootstrapService._bootstrap_attempted = True
        LocalBootstrapService._bootstrap_error = None

        with patch('app.dependencies.local_agent_auth.get_settings') as mock_get_settings:
            mock_settings = Settings()
            mock_settings.LOCAL_MODE = True
            mock_settings.LOCAL_AGENT_KEY = "test-key-12345"
            mock_get_settings.return_value = mock_settings

            client = TestClient(app)
            # Missing header
            response = client.post(
                "/api/v1/agents/register-local",
                json={"hostname": "test-agent", "version": "1.0.0"},
            )

            assert response.status_code == 422  # FastAPI validation error for missing header

    def test_register_local_rejects_invalid_key(self):
        """Verify /register-local returns 401 with wrong LOCAL_AGENT_KEY."""
        # Reset bootstrap state
        LocalBootstrapService._local_tenant_id = uuid4()
        LocalBootstrapService._bootstrap_attempted = True
        LocalBootstrapService._bootstrap_error = None

        with patch('app.dependencies.local_agent_auth.get_settings') as mock_get_settings:
            mock_settings = Settings()
            mock_settings.LOCAL_MODE = True
            mock_settings.LOCAL_AGENT_KEY = "correct-key"
            mock_get_settings.return_value = mock_settings

            client = TestClient(app)
            response = client.post(
                "/api/v1/agents/register-local",
                json={"hostname": "test-agent", "version": "1.0.0"},
                headers={"X-Local-Agent-Key": "wrong-key"},
            )

            assert response.status_code == 401

    def test_register_local_503_when_not_bootstrapped(self):
        """Verify /register-local returns 503 if local tenant not bootstrapped."""
        # Bootstrap not complete
        LocalBootstrapService._local_tenant_id = None
        LocalBootstrapService._bootstrap_attempted = False
        LocalBootstrapService._bootstrap_error = None

        with patch('app.dependencies.local_agent_auth.get_settings') as mock_get_settings:
            mock_settings = Settings()
            mock_settings.LOCAL_MODE = True
            mock_settings.LOCAL_AGENT_KEY = "test-key"
            mock_get_settings.return_value = mock_settings

            client = TestClient(app)
            response = client.post(
                "/api/v1/agents/register-local",
                json={"hostname": "test-agent", "version": "1.0.0"},
                headers={"X-Local-Agent-Key": "test-key"},
            )

            assert response.status_code == 503
            assert "Retry-After" in response.headers
