import pytest

from app.runner.auth import (
    TaskTokenExpired,
    TaskTokenInvalid,
    TaskTokenIssuer,
    TaskTokenVerifier,
    node_digest,
)


NODE = {
    "id": "read_orders",
    "type": "sql.query",
    "data_source_uid": "01900000-0000-7000-8000-000000000010",
    "purpose": "read",
    "config": {
        "statement": "SELECT * FROM orders WHERE day = :day",
        "parameters": {"day": "${parameters.day}"},
    },
}


def test_task_token_is_short_lived_and_bound_to_one_node():
    clock = lambda: 1_000
    issuer = TaskTokenIssuer("x" * 32, clock=clock, ttl_seconds=60)
    verifier = TaskTokenVerifier("x" * 32, clock=clock)

    token = issuer.issue(
        task_uid="01900000-0000-7000-8000-000000000011",
        dataflow_uid="01900000-0000-7000-8000-000000000012",
        workflow_version=7,
        correlation_id="01900000-0000-7000-8000-000000000013",
        node=NODE,
    )
    claims = verifier.verify(token, node=NODE)

    assert claims.node_id == "read_orders"
    assert claims.node_type == "sql.query"
    assert claims.purpose == "read"
    assert claims.node_digest == node_digest(NODE)
    assert claims.expires_at == 1_060
    assert claims.jti
    assert "password" not in token.lower()


def test_task_token_rejects_expiry_tampering_and_other_node():
    now = [1_000]
    issuer = TaskTokenIssuer("x" * 32, clock=lambda: now[0], ttl_seconds=10)
    verifier = TaskTokenVerifier("x" * 32, clock=lambda: now[0])
    token = issuer.issue(
        task_uid="01900000-0000-7000-8000-000000000011",
        dataflow_uid="01900000-0000-7000-8000-000000000012",
        workflow_version=7,
        correlation_id="01900000-0000-7000-8000-000000000013",
        node=NODE,
    )

    changed = {**NODE, "config": {"statement": "DELETE FROM orders"}}
    with pytest.raises(TaskTokenInvalid, match="node binding"):
        verifier.verify(token, node=changed)

    with pytest.raises(TaskTokenInvalid):
        verifier.verify(token[:-1] + ("a" if token[-1] != "a" else "b"), node=NODE)

    now[0] = 1_011
    with pytest.raises(TaskTokenExpired):
        verifier.verify(token, node=NODE)


def test_task_token_secret_has_a_minimum_strength():
    with pytest.raises(ValueError, match="at least 32"):
        TaskTokenIssuer("too-short")
