import json
import os
import sqlite3
import subprocess
import sys
import tempfile
import time
import unittest
from pathlib import Path

from core.service_wrapper import PROJECT_ROOT, RelayServiceWrapper


class TestRelayServiceWrapper(unittest.TestCase):
    def setUp(self):
        self.tempdir = tempfile.TemporaryDirectory()
        root = Path(self.tempdir.name)
        self.output_path = root / "relay.jsonl"
        self.db_path = root / "events.db"
        self.runtime_dir = root / "runtime"
        self.config_path = root / "config.json"
        self.config_path.write_text(
            json.dumps({
                "Enabled": True,
                "Telemetry": {
                    "Path": str(self.output_path),
                    "DatabasePath": str(self.db_path),
                    "PollIntervalSeconds": 1,
                    "HeartbeatIntervalSeconds": 5,
                },
            }),
            encoding="utf-8",
        )
        self.wrapper = RelayServiceWrapper(self.config_path, self.runtime_dir)

    def tearDown(self):
        try:
            self.wrapper.stop(timeout=3)
        finally:
            self.tempdir.cleanup()

    def _cli(self, action):
        completed = subprocess.run(
            [sys.executable, "-m", "core.service_wrapper", action, "--config", str(self.config_path),
             "--runtime-dir", str(self.runtime_dir)],
            cwd=PROJECT_ROOT, capture_output=True, text=True, check=True, timeout=15,
        )
        return json.loads(completed.stdout)

    def test_status_reports_required_runtime_state(self):
        state = self.wrapper.status()
        self.assertEqual(False, state["running"])
        self.assertEqual(True, state["enabled"])
        self.assertIsNone(state["pid"])
        self.assertEqual(1, state["poll_interval"])
        self.assertEqual(str(self.output_path), state["output_path"])

    def test_persistent_start_status_duplicate_stop_and_telemetry(self):
        started = self._cli("--start")
        pid = started["pid"]
        self.assertTrue(started["running"])
        self.assertGreater(pid, 0)

        time.sleep(0.25)  # The --start parent has exited; the worker must remain.
        observed = self._cli("--status")
        self.assertTrue(observed["running"])
        self.assertEqual(pid, observed["pid"])

        duplicate = self._cli("--start")
        self.assertEqual(pid, duplicate["pid"])
        self.assertTrue(self.output_path.exists())
        lines = [json.loads(line) for line in self.output_path.read_text(encoding="utf-8").splitlines()]
        self.assertEqual(1, sum(item["event"] == "relay_started" for item in lines))

        stopped = self._cli("--stop")
        self.assertFalse(stopped["running"])
        self.assertIsNone(stopped["pid"])
        self.assertFalse(self.wrapper.state_path.exists())
        self.assertFalse(self.wrapper.lock_path.exists())
        lines = [json.loads(line) for line in self.output_path.read_text(encoding="utf-8").splitlines()]
        self.assertTrue(any(item["event"] == "relay_stopped" for item in lines))
        connection = sqlite3.connect(self.db_path)
        try:
            event_types = {row[0] for row in connection.execute("SELECT event_type FROM events")}
        finally:
            connection.close()
        self.assertIn("relay_started", event_types)
        self.assertIn("relay_stopped", event_types)

    def test_cleanup_removes_stale_runtime_state(self):
        self.runtime_dir.mkdir()
        self.wrapper.lock_path.mkdir()
        self.wrapper.lock_owner_path.write_text("99999999", encoding="ascii")
        self.wrapper.state_path.write_text(json.dumps({"pid": 99999999}), encoding="utf-8")
        state = self.wrapper.status()
        self.assertFalse(state["running"])
        self.assertFalse(self.wrapper.state_path.exists())
        self.assertFalse(self.wrapper.lock_path.exists())

    def test_string_output_path_is_normalized(self):
        event = {"timestamp": "2026-01-01T00:00:00Z", "source": "relay_service",
                 "event": "process_started", "pid": 42, "image": "python.exe"}
        self.wrapper.service.append_jsonl(event)
        self.assertTrue(self.output_path.exists())
        self.assertEqual(str(self.output_path), str(self.wrapper.service.output_path))


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