from __future__ import annotations

from pathlib import Path

import pytest
from fastapi import HTTPException

from yuxi.services import thread_files_service as svc


@pytest.mark.asyncio
async def test_resolve_thread_artifact_view_blocks_symlink_escape(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
    thread_root = tmp_path / "threads" / "thread-1" / "user-data"
    uploads_dir = thread_root / "uploads"
    uploads_dir.mkdir(parents=True, exist_ok=True)
    outside_file = tmp_path / "outside.txt"
    outside_file.write_text("secret", encoding="utf-8")
    (uploads_dir / "escape.txt").symlink_to(outside_file)

    class _Conversation:
        uid = "user-1"

    async def _fake_require_user_conversation(_repo, _thread_id: str, _current_uid: str):
        return _Conversation()

    monkeypatch.setattr(svc, "require_user_conversation", _fake_require_user_conversation)
    monkeypatch.setattr(svc, "ensure_thread_dirs", lambda _thread_id, _uid: None)
    monkeypatch.setattr(
        svc,
        "sandbox_workspace_dir",
        lambda _thread_id, _uid: tmp_path / "shared" / _uid / "workspace",
    )
    monkeypatch.setattr(svc, "sandbox_uploads_dir", lambda _thread_id: uploads_dir)
    monkeypatch.setattr(svc, "sandbox_outputs_dir", lambda _thread_id: thread_root / "outputs")
    monkeypatch.setattr(svc, "resolve_virtual_path", lambda _thread_id, _path, *, uid: uploads_dir / "escape.txt")
    monkeypatch.setattr(svc, "ConversationRepository", lambda _db: object())

    with pytest.raises(HTTPException, match="access denied"):
        await svc.resolve_thread_artifact_view(
            thread_id="thread-1",
            current_uid="user-1",
            db=None,
            path="/home/gem/user-data/uploads/escape.txt",
        )
