"""Unit tests for centralized permission checks.

Tests all permission functions in app.core.permissions using mock User/Tree/Step objects.
No database required.
"""

from unittest.mock import MagicMock
from uuid import uuid4

from app.core.permissions import (
    ROLE_HIERARCHY,
    can_access_tree,
    can_create_category,
    can_create_content,
    can_create_step_category,
    can_create_tag,
    can_delete_tree,
    can_edit_step,
    can_edit_tree,
    can_manage_category,
    can_manage_step_category,
    can_manage_tree_tags,
    can_view_step,
    get_effective_role,
    has_minimum_role,
)


# --- Helpers ---

def _make_user(
    account_role="engineer",
    is_super_admin=False,
    account_id=None,
    user_id=None,
):
    user = MagicMock()
    user.id = user_id or uuid4()
    user.account_role = account_role
    user.is_super_admin = is_super_admin
    user.account_id = account_id or uuid4()
    return user


def _make_tree(author_id=None, account_id=None, is_default=False, is_public=False):
    tree = MagicMock()
    tree.author_id = author_id or uuid4()
    tree.account_id = account_id or uuid4()
    tree.is_default = is_default
    tree.is_public = is_public
    return tree


def _make_step(created_by=None, visibility="team", account_id=None):
    step = MagicMock()
    step.created_by = created_by or uuid4()
    step.visibility = visibility
    step.account_id = account_id or uuid4()
    return step


def _make_category(account_id=None):
    cat = MagicMock()
    cat.account_id = account_id or uuid4()
    return cat


# --- Role Hierarchy ---


class TestRoleHierarchy:

    def test_hierarchy_order(self):
        assert ROLE_HIERARCHY["super_admin"] > ROLE_HIERARCHY["owner"]
        assert ROLE_HIERARCHY["owner"] > ROLE_HIERARCHY["engineer"]
        assert ROLE_HIERARCHY["engineer"] > ROLE_HIERARCHY["viewer"]

    def test_get_effective_role_super_admin(self):
        user = _make_user(is_super_admin=True, account_role="engineer")
        assert get_effective_role(user) == "super_admin"

    def test_get_effective_role_owner(self):
        user = _make_user(account_role="owner")
        assert get_effective_role(user) == "owner"

    def test_get_effective_role_engineer(self):
        user = _make_user(account_role="engineer")
        assert get_effective_role(user) == "engineer"

    def test_get_effective_role_viewer(self):
        user = _make_user(account_role="viewer")
        assert get_effective_role(user) == "viewer"

    def test_has_minimum_role_super_admin_passes_all(self):
        user = _make_user(is_super_admin=True)
        for role in ["viewer", "engineer", "owner", "super_admin"]:
            assert has_minimum_role(user, role)

    def test_has_minimum_role_viewer_fails_engineer(self):
        user = _make_user(account_role="viewer")
        assert not has_minimum_role(user, "engineer")

    def test_has_minimum_role_engineer_passes_engineer(self):
        user = _make_user(account_role="engineer")
        assert has_minimum_role(user, "engineer")

    def test_has_minimum_role_unknown_role_returns_false(self):
        user = _make_user(account_role="unknown_role")
        assert not has_minimum_role(user, "engineer")


# --- Content Creation ---


class TestCanCreateContent:

    def test_engineer_can_create(self):
        assert can_create_content(_make_user(account_role="engineer"))

    def test_owner_can_create(self):
        assert can_create_content(_make_user(account_role="owner"))

    def test_super_admin_can_create(self):
        assert can_create_content(_make_user(is_super_admin=True))

    def test_viewer_cannot_create(self):
        assert not can_create_content(_make_user(account_role="viewer"))


# --- Tree Permissions ---


class TestCanEditTree:

    def test_author_can_edit_own_tree(self):
        user = _make_user()
        tree = _make_tree(author_id=user.id)
        assert can_edit_tree(user, tree)

    def test_super_admin_can_edit_any_tree(self):
        user = _make_user(is_super_admin=True)
        tree = _make_tree()
        assert can_edit_tree(user, tree)

    def test_account_owner_can_edit_account_tree(self):
        acct = uuid4()
        user = _make_user(account_role="owner", account_id=acct)
        tree = _make_tree(account_id=acct)
        assert can_edit_tree(user, tree)

    def test_engineer_cannot_edit_others_tree(self):
        user = _make_user()
        tree = _make_tree()  # different author and account
        assert not can_edit_tree(user, tree)

    def test_viewer_cannot_edit_any_tree(self):
        user = _make_user(account_role="viewer")
        tree = _make_tree(author_id=user.id)  # even own tree
        assert not can_edit_tree(user, tree)

    def test_owner_cannot_edit_other_account_tree(self):
        user = _make_user(account_role="owner", account_id=uuid4())
        tree = _make_tree(account_id=uuid4())  # different account
        assert not can_edit_tree(user, tree)

    def test_owner_with_none_account_cannot_edit(self):
        user = _make_user(account_role="owner", account_id=None)
        tree = _make_tree(account_id=None)
        assert not can_edit_tree(user, tree)


class TestCanDeleteTree:

    def test_super_admin_can_delete(self):
        user = _make_user(is_super_admin=True)
        assert can_delete_tree(user, _make_tree())

    def test_owner_cannot_delete(self):
        user = _make_user(account_role="owner")
        assert not can_delete_tree(user, _make_tree())

    def test_engineer_cannot_delete(self):
        user = _make_user()
        assert not can_delete_tree(user, _make_tree(author_id=user.id))


class TestCanAccessTree:

    def test_default_tree_accessible_to_all(self):
        user = _make_user(account_role="viewer")
        tree = _make_tree(is_default=True)
        assert can_access_tree(user, tree)

    def test_public_tree_accessible_to_all(self):
        user = _make_user(account_role="viewer")
        tree = _make_tree(is_public=True)
        assert can_access_tree(user, tree)

    def test_author_can_access_own_tree(self):
        user = _make_user()
        tree = _make_tree(author_id=user.id)
        assert can_access_tree(user, tree)

    def test_same_account_can_access(self):
        acct = uuid4()
        user = _make_user(account_id=acct)
        tree = _make_tree(account_id=acct)
        assert can_access_tree(user, tree)

    def test_super_admin_can_access_any(self):
        user = _make_user(is_super_admin=True)
        tree = _make_tree()
        assert can_access_tree(user, tree)

    def test_different_account_cannot_access_private(self):
        user = _make_user(account_id=uuid4())
        tree = _make_tree(account_id=uuid4())
        assert not can_access_tree(user, tree)

    def test_none_account_cannot_access_by_account(self):
        user = _make_user(account_id=None)
        tree = _make_tree(account_id=None, is_public=False, is_default=False)
        assert not can_access_tree(user, tree)


# --- Step Permissions ---


class TestCanEditStep:

    def test_creator_can_edit(self):
        user = _make_user()
        step = _make_step(created_by=user.id)
        assert can_edit_step(user, step)

    def test_super_admin_can_edit_any(self):
        user = _make_user(is_super_admin=True)
        assert can_edit_step(user, _make_step())

    def test_engineer_cannot_edit_others(self):
        user = _make_user()
        assert not can_edit_step(user, _make_step())

    def test_viewer_cannot_edit(self):
        user = _make_user(account_role="viewer")
        step = _make_step(created_by=user.id)
        assert not can_edit_step(user, step)


class TestCanViewStep:

    def test_public_step_visible_to_all(self):
        user = _make_user(account_role="viewer")
        step = _make_step(visibility="public")
        assert can_view_step(user, step)

    def test_private_step_visible_to_creator(self):
        user = _make_user()
        step = _make_step(visibility="private", created_by=user.id)
        assert can_view_step(user, step)

    def test_private_step_hidden_from_others(self):
        user = _make_user()
        step = _make_step(visibility="private")
        assert not can_view_step(user, step)

    def test_team_step_visible_to_same_account(self):
        acct = uuid4()
        user = _make_user(account_id=acct)
        step = _make_step(visibility="team", account_id=acct)
        assert can_view_step(user, step)

    def test_team_step_hidden_from_other_account(self):
        user = _make_user(account_id=uuid4())
        step = _make_step(visibility="team", account_id=uuid4())
        assert not can_view_step(user, step)

    def test_team_step_visible_to_super_admin(self):
        user = _make_user(is_super_admin=True, account_id=uuid4())
        step = _make_step(visibility="team", account_id=uuid4())
        assert can_view_step(user, step)

    def test_unknown_visibility_returns_false(self):
        user = _make_user()
        step = _make_step(visibility="unknown")
        assert not can_view_step(user, step)

    def test_team_step_none_account_returns_false(self):
        user = _make_user(account_id=None)
        step = _make_step(visibility="team", account_id=None)
        assert not can_view_step(user, step)


# --- Tag Permissions ---


class TestCanCreateTag:

    def test_super_admin_can_create_global(self):
        user = _make_user(is_super_admin=True)
        assert can_create_tag(user, account_id=None)

    def test_super_admin_can_create_any_account(self):
        user = _make_user(is_super_admin=True)
        assert can_create_tag(user, account_id=uuid4())

    def test_engineer_can_create_own_account(self):
        user = _make_user()
        assert can_create_tag(user, account_id=user.account_id)

    def test_engineer_cannot_create_global(self):
        user = _make_user()
        assert not can_create_tag(user, account_id=None)

    def test_viewer_cannot_create(self):
        user = _make_user(account_role="viewer")
        assert not can_create_tag(user, account_id=user.account_id)


# --- Category Permissions ---


class TestCanManageCategory:

    def test_super_admin_can_manage_any(self):
        user = _make_user(is_super_admin=True)
        assert can_manage_category(user, _make_category())

    def test_owner_can_manage_own_account(self):
        acct = uuid4()
        user = _make_user(account_role="owner", account_id=acct)
        assert can_manage_category(user, _make_category(account_id=acct))

    def test_owner_cannot_manage_other_account(self):
        user = _make_user(account_role="owner")
        assert not can_manage_category(user, _make_category())

    def test_engineer_cannot_manage(self):
        user = _make_user()
        assert not can_manage_category(user, _make_category())


class TestCanCreateCategory:

    def test_super_admin_can_create_global(self):
        assert can_create_category(_make_user(is_super_admin=True), None)

    def test_owner_can_create_for_own_account(self):
        acct = uuid4()
        user = _make_user(account_role="owner", account_id=acct)
        assert can_create_category(user, acct)

    def test_owner_cannot_create_for_other_account(self):
        user = _make_user(account_role="owner")
        assert not can_create_category(user, uuid4())

    def test_engineer_cannot_create(self):
        assert not can_create_category(_make_user(), uuid4())

    def test_owner_with_none_account_cannot_create(self):
        user = _make_user(account_role="owner", account_id=None)
        assert not can_create_category(user, None)


# --- Tree Tag Permissions ---


class TestCanManageTreeTags:

    def test_author_can_manage(self):
        user = _make_user()
        tree = _make_tree(author_id=user.id)
        assert can_manage_tree_tags(user, tree)

    def test_super_admin_can_manage(self):
        assert can_manage_tree_tags(_make_user(is_super_admin=True), _make_tree())

    def test_owner_can_manage_account_tree(self):
        acct = uuid4()
        user = _make_user(account_role="owner", account_id=acct)
        tree = _make_tree(account_id=acct)
        assert can_manage_tree_tags(user, tree)

    def test_viewer_cannot_manage(self):
        user = _make_user(account_role="viewer")
        tree = _make_tree(author_id=user.id)
        assert not can_manage_tree_tags(user, tree)

    def test_engineer_cannot_manage_others(self):
        assert not can_manage_tree_tags(_make_user(), _make_tree())


# --- Step Category Permissions ---


class TestCanManageStepCategory:

    def test_super_admin_can_manage(self):
        assert can_manage_step_category(_make_user(is_super_admin=True), _make_category())

    def test_owner_can_manage_own_account(self):
        acct = uuid4()
        user = _make_user(account_role="owner", account_id=acct)
        assert can_manage_step_category(user, _make_category(account_id=acct))

    def test_engineer_cannot_manage(self):
        assert not can_manage_step_category(_make_user(), _make_category())


class TestCanCreateStepCategory:

    def test_super_admin_can_create_global(self):
        assert can_create_step_category(_make_user(is_super_admin=True), None)

    def test_owner_can_create_for_own_account(self):
        acct = uuid4()
        user = _make_user(account_role="owner", account_id=acct)
        assert can_create_step_category(user, acct)

    def test_engineer_cannot_create(self):
        assert not can_create_step_category(_make_user(), uuid4())
