"""
Tests for development authentication routes and security

Tests cover:
- Dev auth route availability based on DEV_MODE
- Session creation without authentication
- Security: 404 when DEV_MODE disabled
- Configuration validation
- Visual warning indicators
"""

import pytest
from starpunk import create_app
from starpunk.auth import verify_session


@pytest.fixture
def dev_app(tmp_path):
    """Create app with DEV_MODE enabled"""
    test_data_dir = tmp_path / "dev_data"
    test_data_dir.mkdir(parents=True, exist_ok=True)

    test_config = {
        "TESTING": True,
        "DATABASE_PATH": test_data_dir / "starpunk.db",
        "DATA_PATH": test_data_dir,
        "NOTES_PATH": test_data_dir / "notes",
        "SESSION_SECRET": "test-secret",
        "SITE_URL": "http://localhost:5000",
        "DEV_MODE": True,
        "DEV_ADMIN_ME": "https://dev.example.com",
    }
    app = create_app(config=test_config)
    yield app


@pytest.fixture
def prod_app(tmp_path):
    """Create app with DEV_MODE disabled (production)"""
    test_data_dir = tmp_path / "prod_data"
    test_data_dir.mkdir(parents=True, exist_ok=True)

    test_config = {
        "TESTING": True,
        "DATABASE_PATH": test_data_dir / "starpunk.db",
        "DATA_PATH": test_data_dir,
        "NOTES_PATH": test_data_dir / "notes",
        "SESSION_SECRET": "test-secret",
        "SITE_URL": "http://localhost:5000",
        "ADMIN_ME": "https://prod.example.com",
        "DEV_MODE": False,
    }
    app = create_app(config=test_config)
    yield app


class TestDevAuthRouteAvailability:
    """Test dev auth routes are only available when DEV_MODE enabled"""

    def test_dev_login_available_when_enabled(self, dev_app):
        """Test /dev/login is available when DEV_MODE=true"""
        client = dev_app.test_client()
        response = client.get("/dev/login", follow_redirects=False)

        # Should redirect to dashboard (successful login)
        assert response.status_code == 302
        assert "/admin/" in response.location

    def test_dev_login_404_when_disabled(self, prod_app):
        """Test /dev/login returns 404 when DEV_MODE=false"""
        client = prod_app.test_client()
        response = client.get("/dev/login")

        # Should return 404 - route doesn't exist
        assert response.status_code == 404

    def test_dev_login_not_accessible_in_production(self, prod_app):
        """Test dev login cannot be accessed in production mode"""
        client = prod_app.test_client()

        # Try various paths
        paths = ["/dev/login", "/dev/auth", "/dev-login"]
        for path in paths:
            response = client.get(path)
            # Should be 404 (dev routes not registered) or redirect to login
            assert response.status_code in [404, 302]


class TestDevAuthFunctionality:
    """Test dev auth creates sessions correctly"""

    def test_dev_login_creates_session(self, dev_app):
        """Test dev login creates a valid session"""
        client = dev_app.test_client()
        response = client.get("/dev/login", follow_redirects=False)

        assert response.status_code == 302

        # Check session cookie was set
        cookies = response.headers.getlist("Set-Cookie")
        assert any("session=" in cookie for cookie in cookies)

    def test_dev_login_session_is_valid(self, dev_app):
        """Test dev login session can be verified"""
        client = dev_app.test_client()
        response = client.get("/dev/login", follow_redirects=False)

        # Extract session token from cookie
        session_token = None
        for cookie in response.headers.getlist("Set-Cookie"):
            if "session=" in cookie:
                session_token = cookie.split("session=")[1].split(";")[0]
                break

        assert session_token is not None

        # Verify session is valid
        with dev_app.app_context():
            session_info = verify_session(session_token)
            assert session_info is not None
            assert session_info["me"] == "https://dev.example.com"

    def test_dev_login_uses_dev_admin_me(self, dev_app):
        """Test dev login uses DEV_ADMIN_ME identity"""
        client = dev_app.test_client()
        response = client.get("/dev/login", follow_redirects=False)

        # Get session token
        session_token = None
        for cookie in response.headers.getlist("Set-Cookie"):
            if "session=" in cookie:
                session_token = cookie.split("session=")[1].split(";")[0]
                break

        # Verify identity
        with dev_app.app_context():
            session_info = verify_session(session_token)
            assert session_info is not None
            assert session_info["me"] == dev_app.config["DEV_ADMIN_ME"]

    def test_dev_login_grants_admin_access(self, dev_app):
        """Test dev login grants access to admin routes"""
        client = dev_app.test_client()

        # Login via dev auth
        response = client.get("/dev/login", follow_redirects=True)
        assert response.status_code == 200

        # Should now be able to access admin
        response = client.get("/admin/")
        assert response.status_code == 200


class TestConfigurationValidation:
    """Test configuration validation for dev mode"""

    def test_dev_mode_requires_dev_admin_me(self, tmp_path):
        """Test DEV_MODE=true requires DEV_ADMIN_ME"""
        test_data_dir = tmp_path / "validation_data"
        test_data_dir.mkdir(parents=True, exist_ok=True)

        test_config = {
            "TESTING": True,
            "DATABASE_PATH": test_data_dir / "starpunk.db",
            "DATA_PATH": test_data_dir,
            "NOTES_PATH": test_data_dir / "notes",
            "SESSION_SECRET": "test-secret",
            "SITE_URL": "http://localhost:5000",
            "DEV_MODE": True,
            "DEV_ADMIN_ME": "",  # Explicitly set to empty string
        }

        with pytest.raises(ValueError, match="DEV_ADMIN_ME"):
            app = create_app(config=test_config)

    def test_production_mode_requires_admin_me(self, tmp_path):
        """Test production mode requires ADMIN_ME"""
        test_data_dir = tmp_path / "prod_validation_data"
        test_data_dir.mkdir(parents=True, exist_ok=True)

        test_config = {
            "TESTING": True,
            "DATABASE_PATH": test_data_dir / "starpunk.db",
            "DATA_PATH": test_data_dir,
            "NOTES_PATH": test_data_dir / "notes",
            "SESSION_SECRET": "test-secret",
            "SITE_URL": "http://localhost:5000",
            "DEV_MODE": False,
            "ADMIN_ME": None,  # Explicitly set to None
        }

        with pytest.raises(ValueError, match="ADMIN_ME"):
            app = create_app(config=test_config)

    def test_dev_mode_allows_missing_admin_me(self, tmp_path):
        """Test DEV_MODE=true doesn't require ADMIN_ME"""
        test_data_dir = tmp_path / "dev_no_admin_data"
        test_data_dir.mkdir(parents=True, exist_ok=True)

        test_config = {
            "TESTING": True,
            "DATABASE_PATH": test_data_dir / "starpunk.db",
            "DATA_PATH": test_data_dir,
            "NOTES_PATH": test_data_dir / "notes",
            "SESSION_SECRET": "test-secret",
            "SITE_URL": "http://localhost:5000",
            "DEV_MODE": True,
            "DEV_ADMIN_ME": "https://dev.example.com",
            # ADMIN_ME not set - should be okay
        }

        # Should not raise
        app = create_app(config=test_config)
        assert app is not None


class TestDevModeWarnings:
    """Test dev mode warning indicators"""

    def test_dev_mode_shows_warning_banner(self, dev_app):
        """Test dev mode shows warning banner on pages"""
        client = dev_app.test_client()
        response = client.get("/")
        assert response.status_code == 200

        # Should have dev mode warning
        assert (
            b"DEVELOPMENT MODE" in response.data
            or b"DEV MODE" in response.data
            or b"Development authentication" in response.data
        )

    def test_dev_mode_warning_on_admin_pages(self, dev_app):
        """Test dev mode warning appears on admin pages"""
        client = dev_app.test_client()

        # Login first
        client.get("/dev/login")

        # Check admin page
        response = client.get("/admin/")
        assert response.status_code == 200
        assert b"DEVELOPMENT MODE" in response.data or b"DEV MODE" in response.data

    def test_production_mode_no_warning(self, prod_app):
        """Test production mode doesn't show dev warning"""
        client = prod_app.test_client()
        response = client.get("/")
        assert response.status_code == 200

        # Should NOT have dev mode warning
        assert b"DEVELOPMENT MODE" not in response.data
        assert b"DEV MODE" not in response.data

    def test_dev_login_page_shows_link(self, dev_app):
        """Test login page shows dev login link when DEV_MODE enabled"""
        client = dev_app.test_client()
        response = client.get("/auth/login")
        assert response.status_code == 200

        # Should have link to dev login
        assert b"/dev/login" in response.data or b"Dev Login" in response.data

    def test_production_login_no_dev_link(self, prod_app):
        """Test login page doesn't show dev link in production"""
        client = prod_app.test_client()
        response = client.get("/auth/login")
        assert response.status_code == 200

        # Should NOT have dev login link
        assert b"/dev/login" not in response.data


class TestSecuritySafeguards:
    """Test security safeguards for dev auth"""

    def test_dev_mode_logs_warning(self, tmp_path, caplog):
        """Test dev mode logs warning on startup"""
        import logging

        caplog.set_level(logging.WARNING)

        # Create new app to trigger startup logging
        test_data_dir = tmp_path / "logging_data"
        test_data_dir.mkdir(parents=True, exist_ok=True)

        test_config = {
            "TESTING": True,
            "DATABASE_PATH": test_data_dir / "starpunk.db",
            "DATA_PATH": test_data_dir,
            "NOTES_PATH": test_data_dir / "notes",
            "SESSION_SECRET": "test-secret",
            "SITE_URL": "http://localhost:5000",
            "DEV_MODE": True,
            "DEV_ADMIN_ME": "https://dev.example.com",
        }
        app = create_app(config=test_config)

        # Check logs
        assert any("DEVELOPMENT" in record.message.upper() for record in caplog.records)

    def test_dev_login_logs_session_creation(self, dev_app, caplog):
        """Test dev login logs session creation"""
        import logging

        caplog.set_level(logging.WARNING)

        client = dev_app.test_client()
        client.get("/dev/login")

        # Should log the session creation
        assert any("DEV MODE" in record.message for record in caplog.records)

    def test_dev_mode_cookie_not_secure(self, dev_app):
        """Test dev mode session cookie is not marked secure (for localhost)"""
        client = dev_app.test_client()
        response = client.get("/dev/login", follow_redirects=False)

        # Check cookie settings
        cookies = response.headers.getlist("Set-Cookie")
        session_cookie = [c for c in cookies if "session=" in c][0]

        # Should have httponly but not secure (for localhost testing)
        assert "HttpOnly" in session_cookie
        # Note: 'Secure' might not be set for dev mode to work with http://localhost


class TestIntegrationFlow:
    """Test complete dev auth integration flow"""

    def test_complete_dev_auth_flow(self, dev_app):
        """Test complete flow: dev login -> admin access -> logout"""
        client = dev_app.test_client()

        # Step 1: Access admin without auth (should redirect to login)
        response = client.get("/admin/", follow_redirects=False)
        assert response.status_code == 302
        assert "/auth/login" in response.location

        # Step 2: Use dev login
        response = client.get("/dev/login", follow_redirects=True)
        assert response.status_code == 200

        # Step 3: Access admin (should work now)
        response = client.get("/admin/")
        assert response.status_code == 200
        assert b"Dashboard" in response.data or b"Admin" in response.data

        # Step 4: Create a note
        response = client.post(
            "/admin/new",
            data={
                "content": "# Dev Auth Test\n\nCreated via dev auth.",
                "published": "on",
            },
            follow_redirects=True,
        )
        assert response.status_code == 200

        # Step 5: Logout
        response = client.post("/auth/logout", follow_redirects=True)
        assert response.status_code == 200

        # Step 6: Verify can't access admin anymore
        response = client.get("/admin/", follow_redirects=False)
        assert response.status_code == 302
