from __future__ import annotations

import shutil
from pathlib import Path
from typing import TYPE_CHECKING

from syft_permissions import (
    ACLRequest,
    AccessLevel,
    User,
)

from syft_perms.explain import PermissionExplanation, explain

if TYPE_CHECKING:
    from syft_perms.syftperm_context import SyftPermContext


class SyftFile:
    def __init__(self, abs_path: Path, perm_context: SyftPermContext):
        self.abs_path = abs_path
        self._perm_context = perm_context

    @property
    def _rel_path(self) -> str:
        return str(self.abs_path.relative_to(self._perm_context.datasite))

    def grant_read_access(self, user: str) -> None:
        self._perm_context.modifier.add_permission_for_user(
            self._rel_path, "read", user
        )

    def grant_write_access(self, user: str) -> None:
        self._perm_context.modifier.add_permission_for_user(
            self._rel_path, "write", user
        )

    def grant_admin_access(self, user: str) -> None:
        self._perm_context.modifier.add_permission_for_user(
            self._rel_path, "admin", user
        )

    def revoke_read_access(self, user: str) -> None:
        self._perm_context.modifier.remove_permission_for_user(
            self._rel_path, "read", user
        )

    def revoke_write_access(self, user: str) -> None:
        self._perm_context.modifier.remove_permission_for_user(
            self._rel_path, "write", user
        )

    def revoke_admin_access(self, user: str) -> None:
        self._perm_context.modifier.remove_permission_for_user(
            self._rel_path, "admin", user
        )

    def has_read_access(self, user: str) -> bool:
        return self._check(AccessLevel.READ, user)

    def has_write_access(self, user: str) -> bool:
        return self._check(AccessLevel.WRITE, user)

    def has_admin_access(self, user: str) -> bool:
        return self._check(AccessLevel.ADMIN, user)

    def explain_permissions(self, user: str) -> PermissionExplanation:
        return explain(self._perm_context, self._rel_path, user)

    def move_file_and_its_permissions(self, new_path: str) -> SyftFile:
        new_abs = self._perm_context.datasite / new_path
        new_abs.parent.mkdir(parents=True, exist_ok=True)

        if self.abs_path.exists():
            shutil.move(str(self.abs_path), str(new_abs))

        self._perm_context.modifier.copy_permissions(self._rel_path, new_abs)
        self._perm_context.modifier.remove_all_rules(self._rel_path)
        self._perm_context._reload()

        return SyftFile(new_abs, self._perm_context)

    def _check(self, level: AccessLevel, user: str) -> bool:
        request = ACLRequest(
            path=self._rel_path,
            level=level,
            user=User(id=user),
        )
        return self._perm_context.service.can_access(request)

    def __repr__(self) -> str:
        return f"SyftFile({self._rel_path})"
