import tempfile
import unittest
from pathlib import Path
import uuid

from hiveos import db as db_module
from hiveos.config import USER_CAPABILITIES_HEADER
from hiveos.db import init_db
from hiveos.intelligence import tracing as tracing_module
from hiveos.session_manager import session_manager
from web_interface import app


class ActionTraceOutcomeTests(unittest.TestCase):
    def setUp(self):
        self._original_trace_db_async = tracing_module.TRACE_DB_ASYNC
        self._original_trace_sampling_enabled = tracing_module.TRACE_SAMPLING_ENABLED
        self._original_trace_log_path = tracing_module.TRACE_LOG_PATH

        self._db_path = Path(tempfile.gettempdir()) / f"hive-test-{uuid.uuid4().hex}.db"
        db_module.DB_PATH = str(self._db_path)
        init_db()

        self._trace_log_path = str(Path(tempfile.gettempdir()) / f"hive-trace-{uuid.uuid4().hex}.log")
        tracing_module.TRACE_LOG_PATH = self._trace_log_path
        tracing_module.TRACE_DB_ASYNC = False
        tracing_module.TRACE_SAMPLING_ENABLED = False

        self._cleanup_sessions()
        self.client = app.test_client()

    def tearDown(self):
        self._cleanup_sessions()
        tracing_module.TRACE_DB_ASYNC = self._original_trace_db_async
        tracing_module.TRACE_SAMPLING_ENABLED = self._original_trace_sampling_enabled
        tracing_module.TRACE_LOG_PATH = self._original_trace_log_path
        try:
            db_path = Path(self._db_path)
            if db_path.exists():
                db_path.unlink()
        except Exception:
            pass
        self._cleanup_trace_files()

    def _cleanup_sessions(self):
        session_ids = list(session_manager.sessions.keys())
        for session_id in session_ids:
            try:
                session_manager.delete_session(session_id)
            except Exception:
                pass

    def _cleanup_trace_files(self):
        base = Path(self._trace_log_path)
        for path in [base] + list(base.parent.glob(f"{base.name}.*")):
            try:
                if path.exists():
                    path.unlink()
            except Exception:
                pass

    def _headers(self, user_id, session_id=None):
        headers = {
            "X-User-ID": user_id,
            USER_CAPABILITIES_HEADER: "admin",
        }
        if session_id:
            headers["X-Session-ID"] = session_id
        return headers

    def _create_session(self, user_id):
        response = self.client.post(
            "/create_session",
            json={"label": "trace-test", "user_id": user_id},
            headers=self._headers(user_id),
        )
        self.assertEqual(200, response.status_code)
        return response.get_json()["session_id"]

    def _find_latest_event(self, event_type, action):
        events = tracing_module.read_trace_events(limit=500, filters={"event_type": event_type})
        for event in reversed(events):
            payload = event.get("payload") or {}
            if payload.get("action") == action:
                return event
        return None

    def test_create_session_emits_action_applied(self):
        user_id = "trace-user-create"
        session_id = self._create_session(user_id)

        event = self._find_latest_event("action_applied", "sessions.create")
        self.assertIsNotNone(event)
        payload = event["payload"]
        self.assertEqual("session", payload.get("resource_type"))
        self.assertEqual(session_id, payload.get("resource_id"))

    def test_runtime_inject_yaml_emits_action_applied(self):
        user_id = "trace-user-inject"
        session_id = self._create_session(user_id)
        yaml_payload = (
            "task_name: trace_task\n"
            "execution_mode: concurrent\n"
            "chunks:\n"
            "  - chunk_id: c1\n"
            "    required_capability: compute.generic\n"
            "    payload: {}\n"
        )

        response = self.client.post(
            "/inject_yaml",
            data=yaml_payload,
            headers=self._headers(user_id, session_id),
        )
        self.assertEqual(200, response.status_code)

        event = self._find_latest_event("action_applied", "runtime.inject_yaml")
        self.assertIsNotNone(event)
        payload = event["payload"]
        self.assertEqual("session", payload.get("resource_type"))
        self.assertIsNotNone(payload.get("task_id"))

    def test_delete_task_missing_emits_action_failed(self):
        user_id = "trace-user-delete"
        session_id = self._create_session(user_id)
        missing_task_id = 999999

        response = self.client.post(
            "/delete_task",
            json={"task_id": missing_task_id},
            headers=self._headers(user_id, session_id),
        )
        self.assertEqual(404, response.status_code)

        event = self._find_latest_event("action_failed", "tasks.delete")
        self.assertIsNotNone(event)
        payload = event["payload"]
        self.assertEqual("task", payload.get("resource_type"))
        self.assertEqual(missing_task_id, payload.get("resource_id"))
        self.assertEqual("task_not_found", payload.get("reason"))

    def test_inject_agents_for_pending_emits_action_applied(self):
        user_id = "trace-user-agent-pending"
        session_id = self._create_session(user_id)

        response = self.client.post(
            "/inject_agents_for_pending",
            json={"max_total": 2, "per_capability": 1},
            headers=self._headers(user_id, session_id),
        )
        self.assertEqual(200, response.status_code)

        event = self._find_latest_event("action_applied", "agents.inject_pending")
        self.assertIsNotNone(event)
        payload = event["payload"]
        self.assertEqual("session", payload.get("resource_type"))
        self.assertIn("injected_total", payload)

    def test_freeze_zone_missing_zone_emits_action_failed(self):
        user_id = "trace-user-freeze-zone"
        session_id = self._create_session(user_id)
        missing_zone_id = "zone-missing"

        response = self.client.post(
            "/freeze_zone",
            json={"zone_id": missing_zone_id},
            headers=self._headers(user_id, session_id),
        )
        self.assertEqual(404, response.status_code)

        event = self._find_latest_event("action_failed", "control.freeze_zone")
        self.assertIsNotNone(event)
        payload = event["payload"]
        self.assertEqual("zone", payload.get("resource_type"))
        self.assertEqual(missing_zone_id, payload.get("resource_id"))
        self.assertEqual("zone_not_found", payload.get("reason"))


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