#!/usr/bin/env python3
"""Regression tests for the no-network Agentic OS teaching demo."""
import copy
import json
import tempfile
import unittest
from pathlib import Path

import agentic_os as oslab


class AgenticOSLabTests(unittest.TestCase):
    def setUp(self):
        self.temp = tempfile.TemporaryDirectory()
        self.addCleanup(self.temp.cleanup)
        self.state = Path(self.temp.name) / "state"
        self.policy = oslab.load_json(oslab.ROOT / "constitution.json")
        self.sample = oslab.load_json(oslab.ROOT / "demo-safe.json")
        self.hostile = oslab.load_json(oslab.ROOT / "demo-injection.json")

    def fixture(self, task, name="case.json"):
        path = Path(self.temp.name) / name
        path.write_text(json.dumps(task), encoding="utf-8")
        return path

    def test_normal_run_creates_draft_only_and_does_not_self_approve(self):
        result = oslab.run(self.fixture(self.sample), self.state, self.policy)
        self.assertEqual(result["status"], "VERIFIED_DRAFT_NEEDS_HUMAN_REVIEW")
        self.assertEqual(result["estimated_cost_usd"], 0.0064)
        self.assertFalse(result["human_approval"])
        self.assertEqual(result["external_actions"], [])
        self.assertEqual(oslab.report(self.state, self.policy)["human_audit_count"], 0)

    def test_untrusted_instruction_is_detected_as_data_not_obeyed(self):
        result = oslab.run(self.fixture(self.hostile), self.state, self.policy)
        self.assertEqual(result["status"], "REJECTED")
        self.assertTrue(any("Suspicious command" in x for x in result["verification"]["reasons"]))
        self.assertFalse(result["human_approval"])

    def test_estimated_budget_gate_refuses_before_work(self):
        policy = copy.deepcopy(self.policy)
        policy["daily_budget_usd"] = 0.001
        policy["max_estimated_task_usd"] = 0.001
        oslab.validate_policy(policy)
        result = oslab.run(self.fixture(self.sample), self.state, policy)
        self.assertEqual(result["status"], "BLOCKED_BUDGET")
        self.assertEqual(oslab.report(self.state, policy)["simulated_daily_estimated_spend_usd"], 0.0)

    def test_run_id_cannot_be_reused(self):
        file = self.fixture(self.sample)
        oslab.run(file, self.state, self.policy)
        with self.assertRaises(oslab.PolicyError):
            oslab.run(file, self.state, self.policy)

    def test_explicit_human_audit_controls_trust(self):
        policy = copy.deepcopy(self.policy)
        policy["min_audited_runs"] = 1
        oslab.run(self.fixture(self.sample), self.state, policy)
        before = oslab.trust(oslab.events(self.state), self.sample["type"], policy)
        self.assertEqual(before["state"], "DRAFT_ONLY")
        passed = oslab.audit(self.sample["id"], "learner", "pass", self.state, policy)
        self.assertEqual(passed["state"], "ELIGIBLE_FOR_HUMAN_REVIEW_OF_NARROW_AUTONOMY")
        self.assertFalse(passed["external_actions_allowed"])
        with self.assertRaises(oslab.PolicyError):
            oslab.audit(self.sample["id"], "learner", "pass", self.state, policy)
        later = copy.deepcopy(self.sample)
        later["id"] = "feedback-safe-002"
        oslab.run(self.fixture(later, "second.json"), self.state, policy)
        revoked = oslab.audit("feedback-safe-002", "learner", "fail", self.state, policy)
        self.assertEqual(revoked["state"], "REVOKED_REQUIRES_REVIEW")

    def test_source_mismatch_is_rejected(self):
        draft = oslab.execute_draft(self.sample, oslab.plan_read_only(self.sample))
        draft["claims"][0]["quote"] = "Invented customer assertion."
        verification = oslab.verify_independently(self.sample, draft)
        self.assertFalse(verification["passed"])
        self.assertIn("Claim does not match original source", verification["reasons"])

    def test_monitor_rechecks_saved_evidence_without_network(self):
        oslab.run(self.fixture(self.sample), self.state, self.policy)
        checks = oslab.monitor(self.state, self.policy)
        self.assertEqual(len(checks), 1)
        self.assertEqual(checks[0]["status"], "CHECKED")

    def test_stricter_policy_fails_closed(self):
        policy = copy.deepcopy(self.policy)
        policy["allow_external_actions"] = True
        with self.assertRaises(oslab.PolicyError):
            oslab.validate_policy(policy)


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