from __future__ import annotations

import sys
import tempfile
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[3]))

from backend.app.api.errors import AppError
from backend.app.security.path_guard import PathGuard


class PathGuardTest(unittest.TestCase):
    def setUp(self) -> None:
        self.temp_dir = tempfile.TemporaryDirectory()
        self.root = Path(self.temp_dir.name) / "root"
        self.root.mkdir(parents=True, exist_ok=True)
        self.other = Path(self.temp_dir.name) / "other"
        self.other.mkdir(parents=True, exist_ok=True)
        self.guard = PathGuard({"storage1": str(self.root)})

    def tearDown(self) -> None:
        self.temp_dir.cleanup()

    def test_resolve_under_whitelisted_root(self) -> None:
        target = self.root / "series"
        target.mkdir()

        resolved = self.guard.resolve_directory_path("storage1/series")

        self.assertEqual(resolved.alias, "storage1")
        self.assertEqual(resolved.relative, "storage1/series")
        self.assertEqual(resolved.absolute, target.resolve())

    def test_rejects_path_traversal(self) -> None:
        with self.assertRaises(AppError) as ctx:
            self.guard.resolve_path("storage1/../etc")

        self.assertEqual(ctx.exception.code, "path_traversal_detected")
        self.assertEqual(ctx.exception.status_code, 403)

    def test_rejects_symlink_escape(self) -> None:
        outside_dir = self.other / "escape"
        outside_dir.mkdir()
        symlink = self.root / "link"
        symlink.symlink_to(outside_dir, target_is_directory=True)

        with self.assertRaises(AppError) as ctx:
            self.guard.resolve_directory_path("storage1/link")

        self.assertEqual(ctx.exception.code, "path_outside_whitelist")
        self.assertEqual(ctx.exception.status_code, 403)


if __name__ == "__main__":
    unittest.main()
