import pytest
import os
import secrets
from hvpdb.core import HVPDB
from hvpdb.webauthn_server import WebAuthnHandler
import http.server
import socketserver
import threading
import time
import requests

def test_path_traversal(tmp_path):
    """Verify Path Traversal vulnerability is fixed."""
    db_path = tmp_path / "test_traversal.hvdb"
    db = HVPDB(str(db_path), "password")
    
    # These should fail
    bad_names = [
        "../bad",
        "..",
        "foo/bar",
        "foo\\bar",
        ".hidden",
        "hidden.",
        "foo..bar"
    ]
    
    for name in bad_names:
        try:
            db.group(name)
            pytest.fail(f"Should have raised ValueError for invalid group name: '{name}'")
        except ValueError as e:
            assert "Invalid group" in str(e)
            
    # These should succeed
    good_names = ["users", "data_2023", "my-group"]
    for name in good_names:
        g = db.group(name)
        assert g.name == name
        
    db.close()

def test_timing_leak_mitigation(tmp_path):
    """Verify verify_password doesn't return instantly for empty hash."""
    db_path = tmp_path / "test_timing.hvp"
    db = HVPDB(str(db_path), "password")
    
    start = time.perf_counter()
    # Verify with empty stored hash (should perform dummy work)
    result = db._verify_password("", "password")
    end = time.perf_counter()
    
    assert result is False
    # It's hard to deterministicly test timing in unit tests without statistical analysis,
    # but we can ensure it runs without error and takes non-zero time.
    # The key is that we called secrets.compare_digest inside.
    
    db.close()

def test_webauthn_csrf_and_origin():
    """Verify WebAuthn server CSRF and Origin checks."""
    # Start a dummy server
    port = 8888
    user = "testuser"
    allowed_origin = "localhost:8888"
    
    # Mock server setup
    handler = lambda *args, **kwargs: WebAuthnHandler(*args, username=user, allowed_origin=allowed_origin, **kwargs)
    httpd = socketserver.TCPServer(("localhost", port), handler)
    
    t = threading.Thread(target=httpd.serve_forever)
    t.daemon = True
    t.start()
    
    try:
        base_url = f"http://localhost:{port}"
        
        # 1. Test Registration Page (GET) - Should have CSRF token
        res = requests.get(f"{base_url}/webauthn/register")
        assert res.status_code == 200
        assert 'name="csrf-token"' in res.text
        
        # Extract token
        import re
        match = re.search(r'content="([a-f0-9]+)"', res.text)
        assert match
        token = match.group(1)
        
        # 2. Test Challenge (GET) - Origin Check
        # Correct Origin (Host header is set by requests automatically)
        res = requests.get(f"{base_url}/webauthn/challenge")
        assert res.status_code == 200
        
        # Incorrect Origin (Spoofed Host)
        res = requests.get(f"{base_url}/webauthn/challenge", headers={"Host": "evil.com"})
        assert res.status_code == 403
        
        # 3. Test Verify (POST) - CSRF Check
        # Missing Token
        res = requests.post(f"{base_url}/webauthn/verify", json={})
        assert res.status_code == 403
        
        # Invalid Token
        res = requests.post(f"{base_url}/webauthn/verify", json={}, headers={"X-CSRF-Token": "badtoken"})
        assert res.status_code == 403
        
        # Valid Token (but invalid body, which is fine for this test)
        # Note: The server tries to read content-length and parse JSON.
        # If we send empty JSON, it might fail later, but pass CSRF check.
        try:
            res = requests.post(f"{base_url}/webauthn/verify", json={}, headers={"X-CSRF-Token": token})
            # It might crash on json access or something else, but NOT 403 CSRF
            assert res.status_code != 403
        except:
            pass
            
    finally:
        httpd.shutdown()
        httpd.server_close()
