import base64
import subprocess
import sys

import pytest
from app.config import Settings
from app.security import canonical_payload_hash, hash_password, payload_matches, verify_password
from hayva_ai.config import AISettings
from hayva_computer.config import Settings as ComputerSettings


def test_password_round_trip_and_wrong_password():
    encoded = hash_password("a correct horse battery staple")
    assert "a correct horse" not in encoded
    assert verify_password("a correct horse battery staple", encoded)
    assert not verify_password("wrong password", encoded)


def test_short_password_rejected():
    with pytest.raises(ValueError):
        hash_password("too-short")


def test_canonical_payload_hash_is_order_independent_and_tamper_evident():
    expected = canonical_payload_hash({"recipient": "Ahmed", "content": "After 4 PM"})
    assert payload_matches({"content": "After 4 PM", "recipient": "Ahmed"}, expected)
    assert not payload_matches({"content": "Before 4 PM", "recipient": "Ahmed"}, expected)


def test_application_secret_can_be_file_mounted_and_missing_secret_fails_closed(tmp_path):
    secret_file = tmp_path / "app-secret"
    secret_file.write_text("s" * 40, encoding="utf-8")
    settings = Settings(
        _env_file=None, app_secret=None, app_secret_file=str(secret_file),
        database_url="sqlite+aiosqlite://", redis_url="redis://localhost:6379/0",
    )
    assert settings.require_app_secret() == "s" * 40
    missing = Settings(
        _env_file=None, app_secret=None, app_secret_file=str(tmp_path / "missing"),
        database_url="sqlite+aiosqlite://", redis_url="redis://localhost:6379/0",
    )
    with pytest.raises(RuntimeError, match="unconfigured"):
        missing.require_app_secret()


def test_security_key_generator_creates_complete_set_and_refuses_overwrite(tmp_path):
    root = tmp_path / "secrets"
    command = [sys.executable, "scripts/generate_security_keys.py", "--secrets-root", str(root)]
    assert subprocess.run(command, check=False, capture_output=True).returncode == 0
    targets = (
        root / "internal" / "app-secret",
        root / "encryption" / "data-encryption-key",
        root / "internal" / "computer-capability-private-key",
        root / "internal" / "computer-capability-public-key",
        root / "internal" / "ai-service-token",
        root / "internal" / "computer-service-token",
        root / "bootstrap" / "owner_token",
    )
    for target in targets:
        encoded = target.read_text(encoding="utf-8").strip()
        assert len(base64.urlsafe_b64decode(encoded + "=" * (-len(encoded) % 4))) == 32
    assert subprocess.run(command, check=False, capture_output=True).returncode != 0


def test_core_production_configuration_requires_mounted_secrets_and_real_dependencies(tmp_path):
    def secret(name: str, value: str) -> str:
        path = tmp_path / name
        path.write_text(value, encoding="utf-8")
        return str(path)

    encoded_key = base64.urlsafe_b64encode(b"k" * 32).decode("ascii").rstrip("=")
    settings = Settings(
        _env_file=None,
        app_env="production",
        app_base_url="https://hayva.company.tld",
        app_secret_file=secret("app", "a" * 40),
        ai_service_token_file=secret("ai", "b" * 40),
        computer_service_token_file=secret("computer", "c" * 40),
        computer_capability_private_key_file=secret("capability", encoded_key),
        data_encryption_key_file=secret("encryption", encoded_key),
        compute_provider="libvirt_gateway",
        compute_provider_url="https://compute.internal.example/v1",
        compute_provider_token_file=secret("compute", "d" * 40),
        database_url="postgresql+asyncpg://hayva_app:strong-secret@postgres:5432/hayva",
        redis_url="redis://hayva:strong-secret@redis:6379/0",
    )
    assert settings.production_configuration_errors() == []

    settings.compute_provider = "unconfigured"
    settings.compute_provider_url = "http://compute.internal.example"
    settings.compute_provider_token_file = None
    errors = settings.production_configuration_errors()
    assert any("COMPUTE_PROVIDER must" in item for item in errors)
    assert any("HTTPS" in item for item in errors)
    assert any("COMPUTE_PROVIDER_TOKEN_FILE" in item for item in errors)

    settings.app_base_url = "http://localhost:8080"
    assert any("APP_BASE_URL" in item for item in settings.production_configuration_errors())


def test_worker_production_configuration_fails_closed(tmp_path):
    token = tmp_path / "service-token"
    token.write_text("s" * 40, encoding="utf-8")
    key = tmp_path / "capability-key"
    key.write_text(
        base64.urlsafe_b64encode(b"p" * 32).decode("ascii").rstrip("="),
        encoding="utf-8",
    )
    provider_key = tmp_path / "provider-key"
    provider_key.write_text("provider-secret", encoding="utf-8")

    ai = AISettings(
        _env_file=None, app_env="production", ai_service_token_file=str(token),
        openai_api_key_file=str(provider_key), openai_model="owner-approved-model",
    )
    assert ai.production_configuration_errors() == []
    computer = ComputerSettings(
        _env_file=None, app_env="production", computer_service_token_file=str(token),
        computer_capability_public_key_file=str(key),
        browser_profile_root="/data/browser-profiles",
        computer_artifact_root="/data/computer-artifacts",
        browser_headless=True,
    )
    assert computer.production_configuration_errors() == []

    ai.openai_model = None
    computer.browser_profile_root = "/tmp/profiles"
    assert ai.production_configuration_errors()
    assert computer.production_configuration_errors()
