import sys
import unittest
from unittest.mock import patch, MagicMock
import gui_2
import pytest
import importlib
from pathlib import Path
from fastapi.testclient import TestClient

class TestHeadlessAPI(unittest.TestCase):
    def setUp(self):
        # We need an App instance to initialize the API, but we want to avoid GUI stuff
        with patch('gui_2.session_logger.open_session'), \
             patch('gui_2.ai_client.set_provider'), \
             patch('gui_2.session_logger.close_session'):
            self.app_instance = gui_2.App()
            # Set a default API key for tests
            self.test_api_key = "test-secret-key"
            self.app_instance.config["headless"] = {"api_key": self.test_api_key}
            self.headers = {"X-API-KEY": self.test_api_key}

            # Clear any leftover state
            self.app_instance._pending_actions = {}
            self.app_instance._pending_dialog = None

            self.api = self.app_instance.create_api()
            self.client = TestClient(self.api)

    def test_health_endpoint(self):
        response = self.client.get("/health")
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json(), {"status": "ok"})

    def test_status_endpoint_unauthorized(self):
        # Ensure a key is required
        with patch.dict(self.app_instance.config, {"headless": {"api_key": "some-required-key"}}):
            response = self.client.get("/status")
            self.assertEqual(response.status_code, 403)

    def test_status_endpoint_authorized(self):
        # We'll use a test key
        headers = {"X-API-KEY": "test-secret-key"}
        with patch.dict(self.app_instance.config, {"headless": {"api_key": "test-secret-key"}}):
            response = self.client.get("/status", headers=headers)
            self.assertEqual(response.status_code, 200)

    def test_generate_endpoint(self):
        payload = {
            "prompt": "Hello AI"
        }
        # Mock ai_client.send and get_comms_log
        with patch('gui_2.ai_client.send') as mock_send, \
             patch('gui_2.ai_client.get_comms_log') as mock_log:
            mock_send.return_value = "Hello from Mock AI"
            mock_log.return_value = [{
                "kind": "response",
                "payload": {
                    "usage": {"input_tokens": 10, "output_tokens": 5}
                }
            }]

            response = self.client.post("/api/v1/generate", json=payload, headers=self.headers)
            self.assertEqual(response.status_code, 200)
            data = response.json()
            self.assertEqual(data["text"], "Hello from Mock AI")
            self.assertIn("metadata", data)
            self.assertEqual(data["usage"]["input_tokens"], 10)

    def test_pending_actions_endpoint(self):
        # Manually add a pending action
        with patch('gui_2.uuid.uuid4', return_value="test-action-id"):
            dialog = gui_2.ConfirmDialog("dir", ".")
            self.app_instance._pending_actions[dialog._uid] = dialog

            response = self.client.get("/api/v1/pending_actions", headers=self.headers)
            self.assertEqual(response.status_code, 200)
            data = response.json()
            self.assertEqual(len(data), 1)
            self.assertEqual(data[0]["action_id"], "test-action-id")

    def test_confirm_action_endpoint(self):
        # Manually add a pending action
        with patch('gui_2.uuid.uuid4', return_value="test-confirm-id"):
            dialog = gui_2.ConfirmDialog("dir", ".")
            self.app_instance._pending_actions[dialog._uid] = dialog

            payload = {"approved": True}
            response = self.client.post("/api/v1/confirm/test-confirm-id", json=payload, headers=self.headers)
            self.assertEqual(response.status_code, 200)
            self.assertTrue(dialog._done)
            self.assertTrue(dialog._approved)

    def test_list_sessions_endpoint(self):
        # Ensure logs directory exists
        Path("logs").mkdir(exist_ok=True)
        # Create a dummy log
        dummy_log = Path("logs/test_session_api.log")
        dummy_log.write_text("dummy content")

        try:
            response = self.client.get("/api/v1/sessions", headers=self.headers)
            self.assertEqual(response.status_code, 200)
            data = response.json()
            self.assertIn("test_session_api.log", data)
        finally:
            if dummy_log.exists():
                dummy_log.unlink()

    def test_get_context_endpoint(self):
        response = self.client.get("/api/v1/context", headers=self.headers)
        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertIn("files", data)
        self.assertIn("screenshots", data)
        self.assertIn("files_base_dir", data)

    def test_endpoint_no_api_key_configured(self):
        # Test the security fix specifically
        with patch.dict(self.app_instance.config, {"headless": {"api_key": ""}}):
            response = self.client.get("/status", headers=self.headers)
            self.assertEqual(response.status_code, 403)
            self.assertEqual(response.json()["detail"], "API Key not configured on server")

class TestHeadlessStartup(unittest.TestCase):

    @patch('gui_2.immapp.run')
    @patch('gui_2.api_hooks.HookServer')
    @patch('gui_2.save_config')
    @patch('gui_2.ai_client.cleanup')
    @patch('uvicorn.run') # Mock uvicorn.run to prevent hanging
    def test_headless_flag_prevents_gui_run(self, mock_uvicorn_run, mock_cleanup, mock_save_config, mock_hook_server, mock_immapp_run):
        # Setup mock argv with --headless
        test_args = ["gui_2.py", "--headless"]

        with patch.object(sys, 'argv', test_args):
            with patch('gui_2.session_logger.close_session'), \
                 patch('gui_2.session_logger.open_session'):
                app = gui_2.App()

                # Mock _fetch_models to avoid network calls
                app._fetch_models = MagicMock()

                app.run()

                # Expectation: immapp.run should NOT be called in headless mode
                mock_immapp_run.assert_not_called()
                # Expectation: uvicorn.run SHOULD be called
                mock_uvicorn_run.assert_called_once()

    @patch('gui_2.immapp.run')
    def test_normal_startup_calls_gui_run(self, mock_immapp_run):
        test_args = ["gui_2.py"]
        with patch.object(sys, 'argv', test_args):
            # In normal mode, it should still call immapp.run
            with patch('gui_2.api_hooks.HookServer'), \
                 patch('gui_2.save_config'), \
                 patch('gui_2.ai_client.cleanup'), \
                 patch('gui_2.session_logger.close_session'), \
                 patch('gui_2.session_logger.open_session'):
                app = gui_2.App()
                app._fetch_models = MagicMock()
                app.run()
                mock_immapp_run.assert_called_once()

def test_fastapi_installed():
    """Verify that fastapi is installed."""
    try:
        importlib.import_module("fastapi")
    except ImportError:
        pytest.fail("fastapi is not installed")

def test_uvicorn_installed():
    """Verify that uvicorn is installed."""
    try:
        importlib.import_module("uvicorn")
    except ImportError:
        pytest.fail("uvicorn is not installed")

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