"""Tests for independent hash chain verification."""

import copy

from langchain_nobulex.audit import AuditLog
from langchain_nobulex.base import ModelResponse, ToolCall
from langchain_nobulex.middleware import NobulexComplianceMiddleware
from langchain_nobulex.verify import verify


class TestVerify:
    def test_empty_log_is_valid(self):
        assert verify([]) is True

    def test_valid_chain(self):
        log = AuditLog()
        log.add_entry(action="read", params={}, allowed=True,
                       matching_rule="r1", timestamp="t1")
        log.add_entry(action="write", params={}, allowed=True,
                       matching_rule="r2", timestamp="t2")
        log.add_entry(action="delete", params={}, allowed=False,
                       matching_rule="r3", timestamp="t3")
        assert verify(log.export_dicts()) is True

    def test_valid_chain_from_entries(self):
        log = AuditLog()
        log.add_entry(action="read", params={}, allowed=True,
                       matching_rule="r1", timestamp="t1")
        assert verify(log.get_log()) is True

    def test_tampered_hash_detected(self):
        log = AuditLog()
        log.add_entry(action="read", params={}, allowed=True,
                       matching_rule="r1", timestamp="t1")
        log.add_entry(action="write", params={}, allowed=True,
                       matching_rule="r2", timestamp="t2")
        dicts = log.export_dicts()
        dicts[0]["hash"] = "tampered" + dicts[0]["hash"][8:]
        assert verify(dicts) is False

    def test_tampered_action_detected(self):
        log = AuditLog()
        log.add_entry(action="read", params={}, allowed=True,
                       matching_rule="r1", timestamp="t1")
        dicts = log.export_dicts()
        dicts[0]["action"] = "delete"
        assert verify(dicts) is False

    def test_tampered_allowed_detected(self):
        log = AuditLog()
        log.add_entry(action="read", params={}, allowed=False,
                       matching_rule="r1", timestamp="t1")
        dicts = log.export_dicts()
        dicts[0]["allowed"] = True
        assert verify(dicts) is False

    def test_tampered_prev_hash_detected(self):
        log = AuditLog()
        log.add_entry(action="read", params={}, allowed=True,
                       matching_rule="r1", timestamp="t1")
        log.add_entry(action="write", params={}, allowed=True,
                       matching_rule="r2", timestamp="t2")
        dicts = log.export_dicts()
        dicts[1]["prev_hash"] = "0" * 64
        assert verify(dicts) is False

    def test_removed_entry_detected(self):
        log = AuditLog()
        log.add_entry(action="a", params={}, allowed=True,
                       matching_rule="r1", timestamp="t1")
        log.add_entry(action="b", params={}, allowed=True,
                       matching_rule="r2", timestamp="t2")
        log.add_entry(action="c", params={}, allowed=True,
                       matching_rule="r3", timestamp="t3")
        dicts = log.export_dicts()
        # Remove middle entry
        del dicts[1]
        assert verify(dicts) is False

    def test_reordered_entries_detected(self):
        log = AuditLog()
        log.add_entry(action="a", params={}, allowed=True,
                       matching_rule="r1", timestamp="t1")
        log.add_entry(action="b", params={}, allowed=True,
                       matching_rule="r2", timestamp="t2")
        dicts = log.export_dicts()
        dicts.reverse()
        assert verify(dicts) is False


class TestVerifyIntegration:
    def test_middleware_audit_verifies(self):
        mw = NobulexComplianceMiddleware(
            rules="permit read; forbid delete;"
        )
        response = ModelResponse(
            tool_calls=[
                ToolCall(name="read", args={"f": "1"}),
                ToolCall(name="delete", args={"id": "2"}),
                ToolCall(name="read", args={"f": "3"}),
            ]
        )
        mw.after_model(response)
        assert verify(mw.get_audit_log()) is True

    def test_middleware_audit_detects_tampering(self):
        mw = NobulexComplianceMiddleware(rules="permit read;")
        response = ModelResponse(
            tool_calls=[ToolCall(name="read", args={})]
        )
        mw.after_model(response)
        log = mw.get_audit_log()
        log[0]["allowed"] = False  # tamper
        assert verify(log) is False
