import pytest

from pygitweb.permissions import (
	PERMISSION_ADD_PROJECTS,
	PERMISSION_SETTINGS,
	Permission,
	PermissionsMap,
	parse_permission_key,
	permission_key,
	project_matches_scope,
)


def test_parse_global_keys() -> None:
	assert parse_permission_key(PERMISSION_ADD_PROJECTS) is not None
	assert parse_permission_key(PERMISSION_SETTINGS) is not None
	assert parse_permission_key("pgw.unknown") is None
	assert parse_permission_key("*") is None


def test_parse_scoped_key() -> None:
	parsed = parse_permission_key("pgw.tasks.my/repo")
	assert parsed is not None
	assert parsed.permission is Permission.TASKS
	assert parsed.project_scope == "my/repo"


def test_permission_key_roundtrip() -> None:
	assert permission_key(Permission.READ, "foo") == "pgw.read.foo"
	assert permission_key(Permission.WRITE, "foo") == "pgw.write.foo"
	assert permission_key(Permission.BOARDS, "foo") == "pgw.boards.foo"
	assert permission_key(Permission.MR_CREATE, "foo") == "pgw.mr.create.foo"
	assert permission_key(Permission.MR_MERGE, "foo") == "pgw.mr.merge.foo"
	assert permission_key(Permission.HOOKS, "foo") == "pgw.hooks.foo"


def test_parse_read_write_scoped_keys() -> None:
	read = parse_permission_key("pgw.read.team/*")
	assert read is not None
	assert read.permission is Permission.READ
	assert read.project_scope == "team/*"
	write = parse_permission_key("pgw.write.private")
	assert write is not None
	assert write.permission is Permission.WRITE
	assert write.project_scope == "private"


def test_parse_mr_scoped_keys() -> None:
	create = parse_permission_key("pgw.mr.create.demo")
	assert create is not None
	assert create.permission is Permission.MR_CREATE
	merge = parse_permission_key("pgw.mr.merge.demo")
	assert merge is not None
	assert merge.permission is Permission.MR_MERGE


def test_project_glob() -> None:
	assert project_matches_scope("team/app", "team/*")
	assert not project_matches_scope("other/app", "team/*")
	assert project_matches_scope("anything", "*")


def test_superuser_grants_all() -> None:
	m = PermissionsMap.model_validate({
		"*": ["admin@example.com"],
		"pgw.tasks.secret": ["other@example.com"],
	})
	assert m.has_permission("admin@example.com", Permission.TASKS, "secret")
	assert m.has_permission("admin@example.com", Permission.SETTINGS)
	assert m.has_permission("other@example.com", Permission.TASKS, "secret")
	assert not m.has_permission("other@example.com", Permission.SETTINGS)
	assert not m.has_permission("other@example.com", Permission.TASKS, "public")


def test_scoped_grant() -> None:
	m = PermissionsMap.model_validate({
		"*": [],
		"pgw.comments.proj-a": ["reader@example.com"],
		"pgw.boards.team/*": ["lead@example.com"],
	})
	assert m.has_permission("reader@example.com", Permission.COMMENTS, "proj-a")
	assert not m.has_permission("reader@example.com", Permission.TASKS, "proj-a")
	assert m.has_permission("lead@example.com", Permission.BOARDS, "team/widget")
	assert not m.has_permission("lead@example.com", Permission.BOARDS, "other/widget")


def test_email_case_insensitive() -> None:
	m = PermissionsMap.model_validate({"*": [], PERMISSION_SETTINGS: ["Admin@Example.COM"]})
	assert m.has_permission("admin@example.com", Permission.SETTINGS)


def test_rejects_unknown_key() -> None:
	with pytest.raises(ValueError, match="unknown permission key"):
		PermissionsMap.model_validate({"pgw.notreal": ["a@b.com"]})


def test_ensures_wildcard_key() -> None:
	m = PermissionsMap.model_validate({PERMISSION_ADD_PROJECTS: ["u@x.com"]})
	assert "*" in m.root


def test_default_access_grants_read_write_to_any_authenticated_user() -> None:
	m = PermissionsMap.default_access()
	assert m.has_permission("alice", Permission.READ, "secret")
	assert m.has_permission("bob@example.com", Permission.WRITE, "team/app")
	assert not m.has_permission("", Permission.READ, "secret")
	assert not m.has_permission(None, Permission.WRITE, "secret")


def test_scoped_read_write_without_wildcard_principal() -> None:
	m = PermissionsMap.model_validate({
		"*": [],
		"pgw.read.public": ["reader@example.com"],
		"pgw.write.public": ["writer@example.com"],
	})
	assert m.has_permission("reader@example.com", Permission.READ, "public")
	assert not m.has_permission("reader@example.com", Permission.WRITE, "public")
	assert m.has_permission("writer@example.com", Permission.WRITE, "public")
	assert not m.has_permission("writer@example.com", Permission.READ, "private")


def test_owner_grants_read_and_write() -> None:
	from unittest.mock import patch

	from pygitweb.auth import can_read_project, can_write_project
	from pygitweb.config import settings
	from pygitweb.sessions import create_session

	with (
		patch.object(settings, "AUTH", True),
		patch("pygitweb.auth.git_get_project_owner", return_value="owner@example.com"),
		patch(
			"pygitweb.auth.auth_config.oauth_permissions",
			PermissionsMap.model_validate({"*": []}),
		),
	):
		token = create_session("someone", email="owner@example.com", auth_method="local")
		assert can_read_project("secret", token)
		assert can_write_project("secret", token)
		assert not can_read_project("secret", None)