"""Per-run CI credential: a GitHub-signed token is accepted only for its own repo and commit."""

import time

import pytest
from jose import jwt
from jose.backends import RSAKey
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

from src.domain.hosting.ci_activation import CiActivationError, verify_ci_run_token

AUD = "p2hs-w4p-activate"
REPO = "Pie-Solutions/proj-repo"
SHA = "a" * 40

_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
_pem = _key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption())
_pub = _key.public_key().public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo)


def _jwks():
    key = RSAKey(_pub, "RS256").to_dict()
    return {"keys": [key]}


def _token(**overrides):
    claims = {
        "iss": "https://token.actions.githubusercontent.com",
        "aud": AUD,
        "repository": REPO,
        "sha": SHA,
        "exp": int(time.time()) + 300,
    }
    claims.update(overrides)
    return jwt.encode(claims, _pem, algorithm="RS256")


def test_accepts_matching_repo_and_sha():
    claims = verify_ci_run_token(_token(), audience=AUD, jwks=_jwks(), expected_repository=REPO, expected_sha=SHA)
    assert claims["sha"] == SHA


def test_rejects_other_repository():
    with pytest.raises(CiActivationError, match="repository_mismatch"):
        verify_ci_run_token(_token(repository="Pie-Solutions/other"), audience=AUD, jwks=_jwks(),
                            expected_repository=REPO, expected_sha=SHA)


def test_rejects_other_commit():
    with pytest.raises(CiActivationError, match="sha_mismatch"):
        verify_ci_run_token(_token(sha="b" * 40), audience=AUD, jwks=_jwks(), expected_repository=REPO, expected_sha=SHA)


def test_rejects_wrong_audience_and_expired():
    with pytest.raises(CiActivationError, match="invalid_ci_token"):
        verify_ci_run_token(_token(aud="someone-else"), audience=AUD, jwks=_jwks(), expected_repository=REPO, expected_sha=SHA)
    with pytest.raises(CiActivationError, match="invalid_ci_token"):
        verify_ci_run_token(_token(exp=int(time.time()) - 10), audience=AUD, jwks=_jwks(), expected_repository=REPO, expected_sha=SHA)


def test_rejects_token_from_another_issuer():
    with pytest.raises(CiActivationError, match="invalid_ci_token"):
        verify_ci_run_token(_token(iss="https://evil.example"), audience=AUD, jwks=_jwks(), expected_repository=REPO, expected_sha=SHA)


def test_fails_closed_without_audience_or_expected_run():
    with pytest.raises(CiActivationError, match="activation_audience_not_configured"):
        verify_ci_run_token(_token(), audience="", jwks=_jwks(), expected_repository=REPO, expected_sha=SHA)
    with pytest.raises(CiActivationError, match="expected_run_unknown"):
        verify_ci_run_token(_token(), audience=AUD, jwks=_jwks(), expected_repository="", expected_sha=SHA)
