from __future__ import annotations

import importlib

from fastapi.testclient import TestClient

from app.minio_provisioning import default_namespace, policy_json_for_backend


def _client(tmp_path, monkeypatch) -> TestClient:
    monkeypatch.setenv("AUTHZ_DATA_DIR", str(tmp_path))
    monkeypatch.setenv("AUTHZ_PRIVATE_KEY_PATH", str(tmp_path / "signing_key.pem"))
    monkeypatch.setenv("AUTHZ_INTERNAL_TOKEN", "test-internal-token")
    import app.main as main

    main = importlib.reload(main)
    return TestClient(main.app)


def _register_backend(client: TestClient) -> None:
    response = client.post(
        "/backends/register",
        json={"backend_id": "alice", "name": "Alice", "base_url": "http://alice.local"},
    )
    assert response.status_code == 200


def test_minio_settings_round_trip_with_masked_public_read(tmp_path, monkeypatch) -> None:
    with _client(tmp_path, monkeypatch) as client:
        _register_backend(client)
        saved = client.post(
            "/backends/alice/settings/minio",
            json={
                "endpoint": "minio.local:9000",
                "access_key": "alice-access",
                "secret_key": "alice-secret",
                "bucket": "beaver-user-files",
                "namespace": "users/alice",
                "secure": True,
                "region": "us-east-1",
            },
        )
        public = client.get("/backends/alice/settings/minio")
        internal = client.get(
            "/internal/backends/alice/settings/minio",
            headers={"Authorization": "Bearer test-internal-token"},
        )

    assert saved.status_code == 200
    assert saved.json()["configured"] is True
    assert saved.json()["endpoint"] == "minio.local:9000"
    assert saved.json()["secret_key_masked"] is True
    assert "secret_key" not in saved.json()
    assert public.status_code == 200
    assert public.json()["access_key"] == "alice-access"
    assert public.json()["bucket"] == "beaver-user-files"
    assert public.json()["namespace"] == "users/alice"
    assert public.json()["secret_key_masked"] is True
    assert "secret_key" not in public.json()
    assert internal.status_code == 200
    assert internal.json()["secret_key"] == "alice-secret"
    assert internal.json()["bucket"] == "beaver-user-files"
    assert internal.json()["namespace"] == "users/alice"


def test_minio_settings_preserve_secret_on_masked_update(tmp_path, monkeypatch) -> None:
    with _client(tmp_path, monkeypatch) as client:
        _register_backend(client)
        first = client.post(
            "/backends/alice/settings/minio",
            json={
                "endpoint": "minio.local:9000",
                "access_key": "alice-access",
                "secret_key": "alice-secret",
                "bucket": "beaver-user-files",
                "namespace": "users/alice",
            },
        )
        updated = client.post(
            "/backends/alice/settings/minio",
            json={
                "endpoint": "minio2.local:9000",
                "access_key": "alice-access-2",
                "secret_key": "",
                "bucket": "beaver-user-files",
                "namespace": "users/alice-v2",
            },
        )
        internal = client.get(
            "/internal/backends/alice/settings/minio",
            headers={"Authorization": "Bearer test-internal-token"},
        )

    assert first.status_code == 200
    assert updated.status_code == 200
    assert updated.json()["endpoint"] == "minio2.local:9000"
    assert updated.json()["namespace"] == "users/alice-v2"
    assert internal.status_code == 200
    assert internal.json()["secret_key"] == "alice-secret"
    assert internal.json()["bucket"] == "beaver-user-files"
    assert internal.json()["namespace"] == "users/alice-v2"


def test_minio_settings_delete_and_missing_behavior(tmp_path, monkeypatch) -> None:
    with _client(tmp_path, monkeypatch) as client:
        _register_backend(client)
        missing_public = client.get("/backends/alice/settings/minio")
        missing_internal = client.get(
            "/internal/backends/alice/settings/minio",
            headers={"Authorization": "Bearer test-internal-token"},
        )
        client.post(
            "/backends/alice/settings/minio",
            json={
                "endpoint": "minio.local:9000",
                "access_key": "alice-access",
                "secret_key": "alice-secret",
                "bucket": "beaver-user-files",
                "namespace": "users/alice",
            },
        )
        deleted = client.delete("/backends/alice/settings/minio")
        after_delete = client.get("/backends/alice/settings/minio")

    assert missing_public.status_code == 200
    assert missing_public.json() == {"configured": False}
    assert missing_internal.status_code == 404
    assert deleted.status_code == 200
    assert deleted.json() == {"ok": True}
    assert after_delete.status_code == 200
    assert after_delete.json() == {"configured": False}


def test_minio_namespace_policy_is_scoped_to_backend_prefix() -> None:
    policy = policy_json_for_backend(backend_id="alice", bucket="beaver-user-files")

    assert default_namespace("alice") == "users/alice"
    assert "arn:aws:s3:::beaver-user-files/users/alice/*" in policy
    assert "users/bob" not in policy
