from pathlib import Path

import yaml


def test_compose_keeps_data_services_private_and_orders_migrations():
    compose = yaml.safe_load(Path("docker-compose.yml").read_text(encoding="utf-8"))
    services = compose["services"]
    assert services["nginx"]["ports"] == ["127.0.0.1:8080:80"]
    assert "ports" not in services["postgres"]
    assert "ports" not in services["redis"]
    assert "ports" not in services["web"]
    assert "ports" not in services["ai-agent"]
    assert "ports" not in services["computer-agent"]
    assert services["nginx"]["depends_on"]["web"]["condition"] == "service_healthy"
    assert services["core-api"]["depends_on"]["db-permissions"]["condition"] == (
        "service_completed_successfully"
    )
    assert services["core-api"]["depends_on"]["ai-agent"]["condition"] == "service_healthy"
    assert services["core-api"]["depends_on"]["computer-agent"]["condition"] == (
        "service_healthy"
    )
    assert services["migrate"]["depends_on"]["postgres"]["condition"] == "service_healthy"
    assert compose["networks"]["app"]["internal"] is True
    assert compose["networks"]["data"]["internal"] is True
    assert "data" not in services["nginx"]["networks"]
    assert services["web"]["networks"] == ["app"]
    assert services["ai-agent"]["networks"] == ["app", "egress"]
    assert "data" not in services["ai-agent"]["networks"]
    assert services["computer-agent"]["networks"] == ["app", "browser-egress"]
    assert "data" not in services["computer-agent"]["networks"]
    assert services["computer-agent"]["read_only"] is True
    assert services["computer-agent"]["cap_drop"] == ["ALL"]
    assert "seccomp=./infra/computer/seccomp_profile.json" in (
        services["computer-agent"]["security_opt"]
    )
    assert services["computer-agent"]["pids_limit"] == 512
    assert services["ai-agent"]["environment"]["OPENAI_API_KEY_FILE"].startswith(
        "/run/secrets/"
    )
    assert "x-service-token" in services["ai-agent"]["healthcheck"]["test"][-1]
    assert services["migrate"]["environment"] == {
        "DATABASE_URL": "${DATABASE_MIGRATION_URL}"
    }
    assert services["db-permissions"]["depends_on"]["migrate"]["condition"] == (
        "service_completed_successfully"
    )
    assert services["core-api"]["environment"]["DATABASE_URL"] == "${DATABASE_URL}"
    assert services["core-api"]["environment"]["APP_BASE_URL"] == "${APP_BASE_URL}"
    assert services["postgres"]["environment"]["POSTGRES_RUNTIME_USER"] == (
        "${POSTGRES_RUNTIME_USER}"
    )
    assert "OWNER_BOOTSTRAP_TOKEN" not in services["core-api"]["environment"]
    assert "APP_SECRET" not in services["core-api"]["environment"]
    assert services["core-api"]["environment"]["APP_SECRET_FILE"].startswith(
        "/run/secrets/"
    )
    assert services["core-api"]["environment"]["OWNER_BOOTSTRAP_TOKEN_FILE"].startswith(
        "/run/secrets/"
    )
    assert services["core-api"]["environment"]["AI_SERVICE_TOKEN_FILE"].startswith(
        "/run/secrets/"
    )
    assert services["core-api"]["environment"]["COMPUTER_SERVICE_TOKEN_FILE"].startswith(
        "/run/secrets/"
    )
    assert services["core-api"]["environment"]["COMPUTE_PROVIDER_TOKEN_FILE"].startswith(
        "/run/secrets/"
    )
    assert any(
        "/run/secrets/compute-provider-token:ro" in volume
        for volume in services["core-api"]["volumes"]
    )
    assert services["computer-agent"]["environment"][
        "COMPUTER_SERVICE_TOKEN_FILE"
    ].startswith("/run/secrets/")
    assert "--aclfile" in services["redis"]["command"]
    assert "REDIS_PASSWORD" not in " ".join(services["redis"]["command"])
    assert services["redis"]["healthcheck"]["test"][-2:] == ["health", "ping"]
    assert "/health/readiness" in services["core-api"]["healthcheck"]["test"][-1]
    for name, service in services.items():
        assert service["logging"]["options"] == {"max-size": "10m", "max-file": "5"}, name
    for name in ("web", "core-api", "ai-agent", "computer-agent", "migrate"):
        assert services[name]["cap_drop"] == ["ALL"]


def test_database_runtime_role_cannot_mutate_security_evidence():
    init_script = Path("infra/postgres/init_runtime_role.sh").read_text(encoding="utf-8")
    grant_script = Path("infra/postgres/apply_runtime_grants.sh").read_text(encoding="utf-8")
    assert "POSTGRES_RUNTIME_USER" in init_script
    assert "NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT" in init_script
    assert "REVOKE CREATE ON SCHEMA public FROM PUBLIC" in init_script
    assert "REVOKE UPDATE, DELETE, TRUNCATE ON audit_events" in grant_script
    assert "REVOKE UPDATE, DELETE, TRUNCATE ON login_events" in grant_script
    assert "REVOKE ALL ON alembic_version" in grant_script


def test_production_gateway_requires_tls_and_applies_browser_security_controls():
    overlay = yaml.safe_load(Path("docker-compose.production.yml").read_text(encoding="utf-8"))
    nginx = Path("infra/nginx/production.conf").read_text(encoding="utf-8")
    assert overlay["services"]["nginx"]["ports"] == ["80:80", "443:443"]
    for service in ("core-api", "ai-agent", "computer-agent"):
        assert overlay["services"][service]["environment"]["APP_ENV"] == "production"
    assert "HAYVA_TLS_DIR" in overlay["services"]["nginx"]["volumes"][1]
    assert "ssl_protocols TLSv1.2 TLSv1.3" in nginx
    assert "Strict-Transport-Security" in nginx
    assert "Content-Security-Policy" in nginx
    assert "cross-site 1" in nginx
    assert "limit_req zone=login" in nginx
    assert "server_tokens off" in nginx
    assert "limit_conn per_client" in nginx
    assert "client_max_body_size 2m" in nginx
    assert "proxy_cookie_flags ~^ secure httponly samesite=strict" in nginx
    assert "Cross-Origin-Opener-Policy" in nginx


def test_backup_and_restore_drill_require_encryption_and_isolation():
    backup = Path("scripts/backup_production.sh").read_text(encoding="utf-8")
    restore = Path("scripts/restore_drill.sh").read_text(encoding="utf-8")
    assert "AGE_RECIPIENT" in backup
    assert "pg_dump" in backup
    assert "browser-profiles" in backup and "computer-artifacts" in backup
    assert "sha256sum" in backup
    assert "AGE_IDENTITY_FILE" in restore
    assert "--network none" in restore
    assert "pg_restore --exit-on-error" in restore


def test_docker_context_excludes_secrets_and_local_state():
    ignored = set(Path(".dockerignore").read_text(encoding="utf-8").splitlines())
    assert {".env", ".git", ".venv", "*.sqlite3", "data", "secrets",
            "**/node_modules", "**/dist"} <= ignored


def test_proxy_overwrites_untrusted_forwarded_address_and_api_trusts_only_internal_proxy():
    nginx = Path("infra/nginx/default.conf").read_text(encoding="utf-8")
    dockerfile = Path("services/core-api/Dockerfile").read_text(encoding="utf-8")
    assert "proxy_set_header X-Forwarded-For $remote_addr;" in nginx
    assert "$proxy_add_x_forwarded_for" not in nginx
    assert '"--forwarded-allow-ips=*"' in dockerfile


def test_gateway_routes_only_versioned_api_and_liveness_to_core():
    nginx = Path("infra/nginx/default.conf").read_text(encoding="utf-8")
    assert "location ^~ /api/" in nginx
    assert "location = /health" in nginx
    assert "proxy_pass http://core-api:8000;" in nginx
    assert "proxy_pass http://web:8080;" in nginx


def test_web_image_uses_locked_build_and_non_root_static_runtime():
    dockerfile = Path("web/Dockerfile").read_text(encoding="utf-8")
    lockfile = Path("web/pnpm-lock.yaml")
    assert "node:24.19.0-alpine3.23 AS build" in dockerfile
    assert "pnpm install --frozen-lockfile" in dockerfile
    assert "USER nginx" in dockerfile
    assert lockfile.is_file()


def test_python_runtime_images_resolve_against_reviewed_exact_constraints():
    constraints = Path("requirements/runtime-constraints.txt").read_text(encoding="utf-8")
    pinned = [line for line in constraints.splitlines() if line and not line.startswith("#")]
    assert pinned
    assert all("==" in line for line in pinned)
    for dockerfile_path in (
        "services/core-api/Dockerfile",
        "services/ai-agent/Dockerfile",
        "services/computer-agent/Dockerfile",
    ):
        dockerfile = Path(dockerfile_path).read_text(encoding="utf-8")
        assert "requirements/runtime-constraints.txt" in dockerfile
        assert "--constraint requirements/runtime-constraints.txt" in dockerfile
