import json
import sys
import tempfile
import unittest
from pathlib import Path
from aria_router.hardened_codex import decode_events, MAX_OUTPUT_BYTES
from aria_router.adapter_contract import ResultStatus
from aria_router.codex_adapter import CodexAdapter
from aria_router.config import ConfigurationError, parse_gateway_config
from aria_router.runner import MockRunner, RunSpec
from tests.helpers import GuardedSubprocessRunner, AdapterHarness, config_dict, fake_codex_template


def events(*items):
    return ('\n'.join(json.dumps(x) for x in items) + '\n').encode()


class HardenedCodexTests(unittest.TestCase):
    def test_success(self):
        r = decode_events(events({'type':'turn.started'}, {'type':'item.completed','item':{'type':'agent_message','text':'OK'}}, {'type':'turn.completed'}))
        self.assertTrue(r.success)
        self.assertEqual(r.output, 'OK')

    def test_tool_event_rejected(self):
        r = decode_events(events({'type':'turn.started'}, {'type':'item.started','item':{'type':'command_execution','command':'id'}}, {'type':'turn.completed'}))
        self.assertEqual(r.error_code, 'TOOL_EVENT_SEEN')

    def test_tool_events_allowed_and_counted_with_allow_tools(self):   # E2b (tools-Profil)
        r = decode_events(events({'type':'turn.started'},
                                 {'type':'item.started','item':{'type':'command_execution','command':'id'}},
                                 {'type':'item.completed','item':{'type':'command_execution','command':'id'}},
                                 {'type':'item.completed','item':{'type':'agent_message','text':'OK'}},
                                 {'type':'turn.completed'}), allow_tools=True)
        self.assertEqual((r.success, r.output, r.tool_seen, r.tool_events), (True, 'OK', True, 1))

    def test_allow_tools_keeps_failure_rules(self):
        r = decode_events(events({'type':'turn.started'},
                                 {'type':'item.completed','item':{'type':'command_execution','command':'id'}},
                                 {'type':'turn.failed'}), allow_tools=True)
        self.assertEqual(r.error_code, 'MODEL_FAILED')
        r = decode_events(events({'type':'turn.started'},
                                 {'type':'item.completed','item':{'type':'command_execution','command':'id'}},
                                 {'type':'turn.completed'}), allow_tools=True)
        self.assertEqual(r.error_code, 'EMPTY_RESPONSE')

    def test_failed_even_if_completed(self):
        r = decode_events(events({'type':'turn.started'}, {'type':'turn.failed'}, {'type':'turn.completed'}))
        self.assertEqual(r.error_code, 'MODEL_FAILED')

    def test_no_turn_started(self):
        self.assertEqual(decode_events(events({'type':'turn.completed'})).error_code, 'INCOMPLETE_TURN')

    def test_output_limit(self):
        self.assertEqual(decode_events(b' ' * (MAX_OUTPUT_BYTES + 1)).error_code, 'OUTPUT_LIMIT')

    def test_bad_utf8(self):
        self.assertEqual(decode_events(b'\xff').error_code, 'INVALID_UTF8')

    def test_invalid_event(self):
        self.assertEqual(decode_events(b'[]\n').error_code, 'INVALID_EVENT')

    # Die folgenden vier Tests prüften HardenedProcessRunner. Der Runner lebt jetzt
    # als SubprocessRunner in runner.py (Env-Allowlist, Prozessgruppe, Limits).
    def test_runner_mock(self):
        with tempfile.TemporaryDirectory() as d:
            spec = RunSpec(tuple(fake_codex_template('success')), b'test', {'PATH': '/usr/bin:/bin'}, 5)
            r = GuardedSubprocessRunner(Path(d)).run(spec)
            self.assertTrue(r.ok)
            self.assertEqual(decode_events(r.stdout).output, 'MOCK_OK')

    def test_runner_timeout(self):
        with tempfile.TemporaryDirectory() as d:
            spec = RunSpec(tuple(fake_codex_template('sleep', '10')), b'test', {'PATH': '/usr/bin:/bin'}, 1)
            r = GuardedSubprocessRunner(Path(d)).run(spec)
            self.assertEqual(r.error_code, 'TIMEOUT')

    def test_invalid_timeout(self):
        with tempfile.TemporaryDirectory() as d:
            with self.assertRaises(ConfigurationError):
                parse_gateway_config(config_dict(Path(d), {'codex': {'timeout_seconds': 0}}))

    def test_prompt_limit(self):
        with tempfile.TemporaryDirectory() as d:
            harness = AdapterHarness(Path(d))
            runner = MockRunner()
            adapter = CodexAdapter(harness.verifier, runner=runner, command_template=fake_codex_template())
            job = harness.job('x' * 140000)
            r = adapter.execute(None, job)
            self.assertEqual((r.status, r.error_code), (ResultStatus.BLOCKED, 'PROMPT_LIMIT'))
            self.assertEqual(runner.calls, [])

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