"""Example 11 — Share a dataset: private by default, then a grant, then published.

Everything the earlier examples built was **private**: readable by you and nobody else.
This is how you change that.

The unit of sharing is the resource, and the mechanism is a **grant** — one resource, one
team, level ``read`` or ``write``. Publishing is not a separate flag: it is a ``read``
grant to the reserved global team ``everyone``, so publishing and withdrawing are the same
verb in both directions and there is nothing special to undo.

The part worth seeing run: **read access on a dataset is enough for its samples.** A team
granted the dataset can read every sample in it without any access to the store it was
built from. That is the case you actually want — share the training set, not the raw
corpus — and it is safe because a dataset is frozen, so the grant cannot silently widen
later.

    DATAVO_SERVER=https://dev.datavo.io python examples/11_share_a_dataset.py
"""

from __future__ import annotations

from _common import (
    build_dataset,
    cleanup,
    cleanup_datasets,
    make_client,
    seed_source_store,
    step,
    unique,
)

from datavo_sdk import KeyGroupSpec, SampleStream, ShardPlan, SourceSpec

KEYS = ["audio.wav", "transcript.txt"]
SAMPLES = [
    {"__key__": f"{i:04d}", "audio.wav": f"audio-{i}".encode(), "transcript.txt": f"utterance {i}"}
    for i in range(1, 6)
]

GLOBAL_TEAM = "everyone"


def describe_sharing(client, dataset: str) -> None:
    """Print the grants and the label a client derives from them."""
    grants = client.list_resource_grants("dataset", dataset)
    shared_with = ", ".join(f"{g.team_id}:{g.level}" for g in grants.grants) or "nobody"
    print(f"    owner={grants.owner_team_id}  sharing={grants.sharing}  shared_with={shared_with}")


def main() -> None:
    client = make_client()
    store = unique("share_store")
    dataset = unique("share_dataset")

    try:
        step("Who am I, and where do new resources land?")
        me = client.get_auth_status()
        # A resource you create lands in your PERSONAL team unless you name one; there
        # is no setting to change that, you name the team at creation.
        print(f"    user={me.user_id}  teams={[team.id for team in me.teams]}")
        for team in me.teams:
            print(f"    team {team.id}{' (implicit)' if team.implicit else ''}")

        step("Build a store and a dataset over it")
        seed_source_store(client, store, samples=SAMPLES)
        build_dataset(
            client,
            dataset,
            sources=[SourceSpec(sample_store=store, keys=KEYS)],
            key_groups=[KeyGroupSpec(name="all", keys=KEYS)],
            shard_plan=ShardPlan(shard_size=100),
        )

        step("Both are private — no grants at all")
        describe_sharing(client, dataset)

        step("Pick a team to share with")
        # Any team that is not the owner and not the global team. On a fresh deployment the
        # bootstrap team is the one that exists, so this picks whatever is available rather
        # than assuming a name.
        owner = client.list_resource_grants("dataset", dataset).owner_team_id
        candidates = [
            team.id
            for team in client.list_teams().teams
            if team.id != owner and team.kind != "global"
        ]
        if not candidates:
            print("    no second team on this deployment — skipping the named-team grant")
        else:
            target = candidates[0]
            step(f"Share the dataset with {target} at read")
            client.grant_resource_access("dataset", dataset, team=target, level="read")
            describe_sharing(client, dataset)
            print(f"    {target} can now read this dataset's samples —")
            print(f"    and still has no access to {store}, the store it was built from.")

            step(f"Unshare it from {target}")
            # The same verb backwards: nothing about revoking is a special case.
            client.revoke_resource_access("dataset", dataset, team=target)
            describe_sharing(client, dataset)

        step("Publish it to everyone")
        # No visibility flag: "public" IS a read grant to the global team.
        client.grant_resource_access("dataset", dataset, team=GLOBAL_TEAM, level="read")
        describe_sharing(client, dataset)

        step("A published dataset still reads exactly the same")
        rows = list(SampleStream.from_dataset(dataset, keys=KEYS, client=client))
        print(f"    read {len(rows)} samples — sharing changes who may read, not what is there")

        step("Withdraw it")
        client.revoke_resource_access("dataset", dataset, team=GLOBAL_TEAM)
        describe_sharing(client, dataset)
    finally:
        cleanup_datasets(client, dataset)
        cleanup(client, store)


if __name__ == "__main__":
    main()
