from __future__ import annotations

import unittest
from pathlib import Path

from mais_humana.catalog import get_platform
from mais_humana.cli import main
from mais_humana.human_rulebook import (
    CANONICAL_PROJECT_ID,
    MCP_CONTROL_PLANE_ID,
    RuleOutcome,
    TruthState,
    evaluate_rulebook,
    iter_rules,
    rulebook_csv,
    rulebook_markdown,
)
from mais_humana.matrix import build_matrix, build_platform_reports
from mais_humana.scanner import scan_platform
from tests.helpers import make_tmp


class HumanRulebookTests(unittest.TestCase):
    def make_repo(self, root: Path, platform_id: str, text: str) -> None:
        platform = get_platform(platform_id)
        repo = root / platform.repo_name
        repo.mkdir(parents=True)
        (repo / "README.md").write_text(text, encoding="utf-8")
        (repo / "src").mkdir()
        (repo / "src" / "index.ts").write_text(
            "\n".join(
                [
                    "export const adminUi = 'panelReady sameSource sourcePayloadHash sourceRecordsHash';",
                    "export const audit = 'traceId auditId actor permission result timestamp';",
                    "export const health = 'readiness responseReady live_readonly screenData screenEvidence';",
                ]
            )
            + "\n",
            encoding="utf-8",
        )

    def make_reports(self, root: Path):
        self.make_repo(
            root,
            "business",
            "business plan entitlement checkout invoice usage admin_ui sameSource panelReady traceId auditId",
        )
        self.make_repo(
            root,
            "identity",
            "identity rbac organization session tenant credentialRef redaction admin_ui sameSource panelReady traceId auditId",
        )
        scans = tuple(scan_platform(root, get_platform(pid)) for pid in ("business", "identity"))
        cells = build_matrix(scans)
        return build_platform_reports(scans, cells)

    def test_generated_rulebook_has_mcp_and_canonical_identity_rules(self) -> None:
        rules = iter_rules()
        self.assertGreater(len(rules), 400)
        self.assertTrue(any(rule.canonical_project_id == CANONICAL_PROJECT_ID for rule in rules))
        self.assertTrue(any(rule.control_plane_id == MCP_CONTROL_PLANE_ID for rule in rules))
        self.assertTrue(any(rule.rule_id == "mais_humana__identity__mcp-only" for rule in rules))

    def test_rulebook_evaluation_produces_coverage_and_exports(self) -> None:
        tmp = make_tmp()
        reports = self.make_reports(tmp)
        report = evaluate_rulebook(reports, limit=40)
        self.assertEqual(len(report.coverage), 40)
        self.assertGreater(report.rules_count, len(report.coverage))
        self.assertTrue(any(item.outcome in {RuleOutcome.COVERED, RuleOutcome.PARTIAL} for item in report.coverage))
        csv_text = rulebook_csv(report)
        markdown = rulebook_markdown(report)
        self.assertIn("rule_id,platform,profile", csv_text)
        self.assertIn("source_payload_hash", csv_text)
        self.assertIn("Rulebook humano-operacional", markdown)
        self.assertIn("evidence-", markdown)
        self.assertIn(CANONICAL_PROJECT_ID, markdown)
        self.assertTrue(all(item.source_payload_hash for item in report.coverage))
        self.assertTrue(all(item.source_records_hash for item in report.coverage))

    def test_docs_formal_exception_stops_catalog_only_from_becoming_blocker(self) -> None:
        tmp = make_tmp()
        self.make_repo(
            tmp,
            "docs",
            "docs catalogOnly formal exception contrato canonicamente governado responseReady ausente",
        )
        scans = tuple(scan_platform(tmp, get_platform(pid)) for pid in ("docs",))
        cells = build_matrix(scans)
        report = evaluate_rulebook(build_platform_reports(scans, cells))
        docs_items = [item for item in report.coverage if item.platform_id == "docs"]
        self.assertTrue(docs_items)
        self.assertTrue(all(item.truth_state == TruthState.FORMAL_EXCEPTION for item in docs_items))
        self.assertTrue(all(item.outcome == RuleOutcome.EXCEPTION for item in docs_items))

    def test_cli_rulebook_returns_success(self) -> None:
        tmp = make_tmp()
        self.make_repo(
            tmp,
            "business",
            "business plan entitlement checkout invoice usage admin_ui sameSource panelReady traceId auditId",
        )
        code = main(["rulebook", "--ecosystem-root", str(tmp), "--limit", "10"])
        self.assertEqual(code, 0)


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