import time

import pytest

from app.core.principal import Principal
from app.integrations.base import InvalidTokenError
from tests.fakes import make_helixtahr_adapter
from tests.tokens import MISSING, meeting_token

adapter = make_helixtahr_adapter()


def test_valid_meeting_token_gives_principal():
    principal = adapter.verify_token(meeting_token())

    assert principal == Principal(
        product="helixtahr", org_id="26", user_id="674", name="Test User", is_org_admin=False
    )


@pytest.mark.parametrize("role_name", ["admin", "superadmin", "Admin", " SuperAdmin "])
def test_admin_and_superadmin_are_org_admins(role_name):
    assert adapter.verify_token(meeting_token(role_name=role_name)).is_org_admin


@pytest.mark.parametrize("role_name", ["manager", "employee", "", 5, MISSING])
def test_other_roles_are_not_org_admins(role_name):
    assert not adapter.verify_token(meeting_token(role_name=role_name)).is_org_admin


def test_role_id_is_ignored():
    # Role IDs differ between environments; only the name counts.
    assert not adapter.verify_token(meeting_token(role_id="1", role_name="manager")).is_org_admin


def test_company_id_as_digit_string_is_accepted():
    assert adapter.verify_token(meeting_token(company_id="26")).org_id == "26"


def _rejected(token: str) -> None:
    with pytest.raises(InvalidTokenError):
        adapter.verify_token(token)


def test_wrong_secret_is_rejected():
    _rejected(meeting_token(secret="some-other-secret-" + "0123456789abcdef" * 3))


@pytest.mark.parametrize("algorithm", ["none", "HS512"])
def test_other_algorithms_are_rejected(algorithm):
    _rejected(meeting_token(algorithm=algorithm))


def test_expired_token_is_rejected():
    _rejected(meeting_token(exp=int(time.time()) - 60))


def test_not_yet_valid_token_is_rejected():
    _rejected(meeting_token(nbf=int(time.time()) + 3600))


def test_wrong_issuer_is_rejected():
    _rejected(meeting_token(iss="https://elsewhere.test/api/v1/login"))


@pytest.mark.parametrize("token_type", ["ai", "", MISSING])
def test_non_meeting_tokens_are_rejected(token_type):
    # The Helix AI token has the same issuer and secret shape but no meeting type.
    _rejected(meeting_token(token_type=token_type))


@pytest.mark.parametrize("claim", ["iss", "sub", "exp", "nbf", "company_id", "name"])
def test_missing_required_claim_is_rejected(claim):
    _rejected(meeting_token(**{claim: MISSING}))


@pytest.mark.parametrize("company_id", [True, None, "", "26a", -1.5, [26]])
def test_invalid_company_id_is_rejected(company_id):
    _rejected(meeting_token(company_id=company_id))


@pytest.mark.parametrize("name", ["", "   ", 123])
def test_invalid_name_is_rejected(name):
    _rejected(meeting_token(name=name))


def test_non_string_sub_is_rejected():
    _rejected(meeting_token(sub=674))


def test_garbage_is_rejected():
    _rejected("not-a-token")
