import pytest

from pygitweb.permissions import (
	PERMISSION_ADD_PROJECTS,
	PERMISSION_SETTINGS,
	Permission,
	PermissionsMap,
	branch_permission_subject,
	has_branch_read_permission,
	has_branch_write_permission,
	parse_permission_key,
	permission_key,
	project_matches_scope,
	short_branch_name_from_ref,
)


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_parse_branch_scoped_keys() -> None:
	read = parse_permission_key("pgw.read_branch.team/app-main")
	assert read is not None
	assert read.permission is Permission.READ_BRANCH
	assert read.project_scope == "team/app-main"
	write = parse_permission_key("pgw.write_branch.dev/alice.*")
	assert write is not None
	assert write.permission is Permission.WRITE_BRANCH
	assert write.project_scope == "dev/alice.*"


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_branch_permission_subject() -> None:
	assert short_branch_name_from_ref("refs/heads/main") == "main"
	assert short_branch_name_from_ref("refs/heads/dev/alice.feature") == "dev/alice.feature"
	assert short_branch_name_from_ref("refs/tags/v1.0.0") is None
	assert branch_permission_subject("demo", "refs/heads/main") == "demo/main"
	assert branch_permission_subject("demo", "refs/tags/v1.0.0") is None


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)


def test_has_branch_write_permission_falls_back_to_project_scope() -> None:
	m = PermissionsMap.model_validate({
		"*": [],
		"pgw.write.demo": ["writer@example.com"],
	})
	assert has_branch_write_permission(m, "writer@example.com", "demo", "refs/heads/main")
	assert not has_branch_write_permission(m, "other@example.com", "demo", "refs/heads/main")


def test_has_branch_write_permission_enforces_branch_specific_rules() -> None:
	m = PermissionsMap.model_validate({
		"*": [],
		"pgw.write.demo": ["writer@example.com"],
		"pgw.write_branch.demo/main": ["branch-writer@example.com"],
	})
	assert has_branch_write_permission(m, "branch-writer@example.com", "demo", "refs/heads/main")
	assert not has_branch_write_permission(m, "writer@example.com", "demo", "refs/heads/main")


def test_has_branch_read_permission_falls_back_to_project_scope() -> None:
	m = PermissionsMap.model_validate({
		"*": [],
		"pgw.read.demo": ["reader@example.com"],
	})
	assert has_branch_read_permission(m, "reader@example.com", "demo", "refs/heads/main")
	assert not has_branch_read_permission(m, "other@example.com", "demo", "refs/heads/main")


def test_has_branch_read_permission_enforces_branch_specific_rules() -> None:
	m = PermissionsMap.model_validate({
		"*": [],
		"pgw.read.demo": ["reader@example.com"],
		"pgw.read_branch.demo/main": ["branch-reader@example.com"],
	})
	assert has_branch_read_permission(m, "branch-reader@example.com", "demo", "refs/heads/main")
	assert not has_branch_read_permission(m, "reader@example.com", "demo", "refs/heads/main")