import unittest
import sys
import os
from pathlib import Path

# Add proposed server's dir and workspace to sys.path
THIS_DIR = Path(__file__).resolve().parent
WORKSPACE_DIR = THIS_DIR.parent.parent.parent
sys.path.insert(0, str(THIS_DIR))
sys.path.insert(0, str(WORKSPACE_DIR))
sys.path.insert(0, str(WORKSPACE_DIR / "05_Production_Pipeline"))
sys.path.insert(0, str(WORKSPACE_DIR / "web_review"))

from starlette.testclient import TestClient
import proposed_server as srv

class TestProposedServer(unittest.TestCase):
    def setUp(self):
        self.client = TestClient(srv.app)

    def test_01_constants_and_app_meta(self):
        self.assertEqual(srv.DEFAULT_PORT, 1515)
        self.assertEqual(srv.DEFAULT_HOST, "0.0.0.0")
        self.assertEqual(srv.app.title, "Thập Ngũ Niên - Cinema Review Studio")

    def test_02_status_endpoint(self):
        resp = self.client.get("/api/status")
        self.assertEqual(resp.status_code, 200)
        data = resp.json()
        self.assertIn("status", data)
        self.assertIn("logs", data)

    def test_03_library_endpoint(self):
        resp = self.client.get("/api/library")
        self.assertEqual(resp.status_code, 200)
        data = resp.json()
        self.assertIn("summary", data)
        self.assertIn("feature_masters", data)
        self.assertIn("raw_scenes", data)

    def test_04_shots_endpoint(self):
        resp = self.client.get("/api/shots")
        self.assertEqual(resp.status_code, 200)
        data = resp.json()
        self.assertIn("shots", data)
        self.assertGreater(len(data["shots"]), 100)

        # Filter by episode
        resp_ep = self.client.get("/api/shots?episode=ep01")
        self.assertEqual(resp_ep.status_code, 200)
        shots_ep = resp_ep.json()["shots"]
        self.assertEqual(len(shots_ep), 188)
        self.assertTrue(all(k.startswith("ep01") for k in shots_ep))

        # Filter by scene
        resp_sc = self.client.get("/api/shots?scene=ep01_scene05")
        self.assertEqual(resp_sc.status_code, 200)
        shots_sc = resp_sc.json()["shots"]
        self.assertTrue(all("ep01_scene05" in k for k in shots_sc))

    def test_05_scenes_endpoint(self):
        resp = self.client.get("/api/scenes")
        self.assertEqual(resp.status_code, 200)
        data = resp.json()
        self.assertIn("scenes", data)

        resp_ep = self.client.get("/api/scenes?episode=ep01")
        self.assertEqual(resp_ep.status_code, 200)

    def test_06_videos_endpoint(self):
        resp = self.client.get("/api/videos")
        self.assertEqual(resp.status_code, 200)
        data = resp.json()
        self.assertIn("videos", data)
        self.assertIn("summary", data)

        resp_empty = self.client.get("/api/videos?category=nonexistent_xyz")
        self.assertEqual(resp_empty.status_code, 200)
        self.assertEqual(resp_empty.json()["videos"], [])

    def test_07_characters_endpoint(self):
        resp = self.client.get("/api/characters")
        self.assertEqual(resp.status_code, 200)
        self.assertIn("groups", resp.json())

    def test_08_prompts_endpoint(self):
        resp = self.client.get("/api/prompts")
        self.assertEqual(resp.status_code, 200)
        self.assertIn("motion_prompts", resp.json())

    def test_09_episodes_and_path_traversal(self):
        resp = self.client.get("/api/episodes")
        self.assertEqual(resp.status_code, 200)
        
        # Valid episode
        resp_valid = self.client.get("/api/episode/TAP_01_XUAN_SAC_THE_NGUYEN_VA_GIONG_BAO_DOAN_TRUONG.md")
        self.assertEqual(resp_valid.status_code, 200)

        # Path traversal guard
        resp_trav = self.client.get("/api/episode/../../windows/system32/cmd.exe")
        self.assertEqual(resp_trav.status_code, 404)

    def test_10_generate_request_validation_and_conflict(self):
        # Validation test
        req1 = srv.GenerateRequest(prompt="valid prompt")
        self.assertEqual(req1.prompt, "valid prompt")
        req2 = srv.GenerateRequest(shot_id="ep01_scene01_shot01")
        self.assertEqual(req2.shot_id, "ep01_scene01_shot01")
        req3 = srv.GenerateRequest(prompt="Test prompt", scene_id="ep01_scene01")
        self.assertEqual(req3.scene_id, "ep01_scene01")

        # Empty body returns 422
        resp_empty = self.client.post("/api/generate", json={})
        self.assertEqual(resp_empty.status_code, 422)

        # Conflict 409 when generating
        prev_status = srv.gen_state["status"]
        try:
            srv.gen_state["status"] = "generating"
            res = self.client.post("/api/generate", json={"prompt": "test"})
            self.assertEqual(res.status_code, 409)

            srv.gen_state["status"] = "concatenating"
            res2 = self.client.post("/api/generate", json={"prompt": "test"})
            self.assertEqual(res2.status_code, 409)
        finally:
            srv.gen_state["status"] = prev_status

    def test_11_concat_endpoint(self):
        # Empty scene_id returns 422
        resp_empty = self.client.post("/api/concat", json={})
        self.assertEqual(resp_empty.status_code, 422)

        # Conflict 409 when generating
        prev_status = srv.gen_state["status"]
        try:
            srv.gen_state["status"] = "generating"
            res = self.client.post("/api/concat", json={"scene_id": "ep01_scene01"})
            self.assertEqual(res.status_code, 409)

            srv.gen_state["status"] = "concatenating"
            res2 = self.client.post("/api/concat", json={"scene_id": "ep01_scene01"})
            self.assertEqual(res2.status_code, 409)

            srv.gen_state["status"] = "idle"
            res3 = self.client.post("/api/concat", json={"scene_id": "ep01_scene01"})
            self.assertEqual(res3.status_code, 200)
            self.assertEqual(res3.json()["status"], "accepted")
        finally:
            srv.gen_state["status"] = prev_status

    def test_12_cache_invalidation_window(self):
        self.assertTrue(srv._cache_time >= 0)

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