import os
import shutil
import tempfile
import importlib
import runpy
import unittest
from pathlib import Path
from urllib.parse import quote
from unittest.mock import patch


TEST_DATA_DIR = tempfile.mkdtemp(prefix="docs-worker-test-")
os.environ["DOCS_WORKER_DATA_DIR"] = TEST_DATA_DIR

docs_app = importlib.import_module("app")


class DownloadDocumentTests(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        docs_app.app.config["TESTING"] = True
        cls.client = docs_app.app.test_client()
        cls.storage_dir = Path(TEST_DATA_DIR) / "storage"

    @classmethod
    def tearDownClass(cls):
        shutil.rmtree(TEST_DATA_DIR, ignore_errors=True)

    def test_download_rejects_traversal_and_symlink_escape(self):
        outside = Path(TEST_DATA_DIR) / "outside.txt"
        outside.write_text("secret", encoding="utf-8")
        self.storage_dir.mkdir(parents=True, exist_ok=True)
        (self.storage_dir / "escape.txt").symlink_to(outside)

        traversal = self.client.get(
            f"/docs/download/{quote('../outside.txt', safe='')}",
            headers={"X-Role": "viewer", "X-Org-Id": "1"},
        )
        self.assertEqual(traversal.status_code, 400)
        self.assertEqual(traversal.get_json(), {"error": "invalid filename"})

        symlink = self.client.get(
            "/docs/download/escape.txt",
            headers={"X-Role": "viewer", "X-Org-Id": "1"},
        )
        self.assertEqual(symlink.status_code, 400)
        self.assertEqual(symlink.get_json(), {"error": "invalid filename"})

    def test_download_returns_existing_file(self):
        self.storage_dir.mkdir(parents=True, exist_ok=True)
        target = self.storage_dir / "statement.txt"
        target.write_text("document-body", encoding="utf-8")

        response = self.client.get(
            "/docs/download/statement.txt",
            headers={"X-Role": "viewer", "X-Org-Id": "1"},
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, b"document-body")

    def test_main_binds_to_loopback(self):
        with patch("flask.Flask.run") as run_mock:
            runpy.run_module("app", run_name="__main__")

        self.assertTrue(run_mock.called)
        _, kwargs = run_mock.call_args
        self.assertEqual(kwargs["host"], "127.0.0.1")
        self.assertEqual(kwargs["debug"], False)


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