"""CLI coverage v4: dispatch fallbacks, branch coverage, and edge cases.

Targets uncovered lines in src/orxaq/experiences/cli.py:
  - 3567, 3579, 3593, 3609, 3622, 3635, 3649, 3662, 3674-3675, 3688,
    3702, 3717, 3728, 3739, 3751-3752, 3762-3763: no-subcommand return 1
  - 3768-3769: unknown command fallback
  - 3773: sys.exit(main()) __main__ entry
  - 126: stress test JSON output
  - 189: fair lending indirect effects loop
  - 700-701: unknown scenario name in credit compare
  - 833-836: worst violations in validate dsep
  - 999, 1001: audit action/actor filter
  - 1020: audit entries display
  - 1027-1032: serve command
  - 1036-1045: edit command
  - 1060: simulate from file
  - 1158: budget WARNING status
  - 1198-1201: providers cache not active
  - 1241-1243, 1245-1248: providers stats latency/failures
  - 1449-1451: model regression not found
  - 1492-1494: model metrics not found
  - 1691-1692: batch run no job_type
  - 1696: batch run with params JSON
  - 1739: batch stats by-status loop
  - 1957-1960: learn compare unchanged metrics
  - 1978-1987: cost summary with usage
  - 2111: db get key-not-found JSON
  - 2311-2314: stream ECL pagination
  - 2382: audit-report full >20 entries
  - 2395-2401: audit-report compliance text
  - 2417: audit-report verify invalid
  - 2449-2450: bulk run no --input
  - 2478-2479: bulk run with errors
  - 2491-2492: bulk dry-run no --input
  - 2536-2538: filing run exception
  - 2632-2634: demo no subcommand
  - 3450-3452: tenant create ValueError
"""

from __future__ import annotations

import argparse
import dataclasses
import json
import runpy
from pathlib import Path
from unittest import mock

import pytest

from orxaq.experiences.cli import main

# ── No-subcommand dispatch fallbacks (lines 3567–3763) ────────────────────────


class TestNoSubcommandFallbacks:
    """When a top-level command is given with no subcommand, parser.parse_args
    is called with --help (raises SystemExit(0) from argparse), and return 1
    is the intended exit status.  We mock sys.exit so that execution falls
    through to `return 1`."""

    @pytest.mark.parametrize(
        "cmd",
        [
            "credit",
            "fabric",
            "providers",
            "model",
            "export",
            "learn",
            "cost",
            "tenant",
            "batch",
            "metrics",
            "db",
            "analytics",
            "filing",
            "stream",
        ],
    )
    def test_subcommand_group_no_arg_returns_1(self, cmd: str, capsys) -> None:
        with mock.patch("sys.exit"):  # argparse --help → sys.exit(0), make it a no-op
            rc = main([cmd])
        assert rc == 1

    def test_audit_report_no_subcommand_returns_1(self, capsys) -> None:
        with mock.patch("sys.exit"):
            rc = main(["audit-report"])
        assert rc == 1

    def test_bulk_no_subcommand_returns_1(self, capsys) -> None:
        with mock.patch("sys.exit"):
            rc = main(["bulk"])
        assert rc == 1


# ── Unknown command fallback (lines 3768-3769) ────────────────────────────────


class TestUnknownCommandFallback:
    """Lines 3768-3769: no if-block matches args.command → print_help(); return 1."""

    def test_unknown_command_returns_1(self, capsys) -> None:
        fake_args = argparse.Namespace(command="zzunknown_xyz", json=False, db=None)
        with mock.patch("orxaq.experiences.cli.build_parser") as mock_bp:
            mock_parser = mock.MagicMock()
            mock_bp.return_value = mock_parser
            mock_parser.parse_args.return_value = fake_args
            rc = main(["zzunknown_xyz"])
        assert rc == 1
        mock_parser.print_help.assert_called_once()


# ── Module __main__ entry (line 3773) ─────────────────────────────────────────


class TestModuleMain:
    """Line 3773: sys.exit(main()) executed when run as __main__."""

    def test_module_entry_point(self) -> None:
        with pytest.raises(SystemExit):
            with mock.patch("orxaq.experiences.cli.main", return_value=0):
                runpy.run_module("orxaq.experiences.cli", run_name="__main__")


# ── Line 126: stress test JSON output ─────────────────────────────────────────


class TestCreditPdStressJson:
    """Line 126: `return _json_out(result)` in credit pd --scenario with --json."""

    def test_credit_pd_stress_json_output(self, capsys) -> None:
        rc = main(["--json", "credit", "pd", "--scenario", "adverse"])
        assert rc == 0
        out = capsys.readouterr().out
        data = json.loads(out)
        assert isinstance(data, dict)


# ── Line 189: fair lending indirect effects loop ──────────────────────────────


class TestFairLendingIndirectEffects:
    """Line 189: `print(f'    via {mediator}: ...')` when indirect effects exist."""

    def test_fairness_indirect_effects(self, capsys) -> None:
        from orxaq.credit.fair_lending import EffectDecomposition

        fake_decomp = EffectDecomposition(
            protected_var="age",
            outcome_var="default_flag",
            total_effect=0.05,
            direct_effect=0.03,
            indirect_effects={"income": -0.01, "loan_amount": 0.03},
            legitimate_paths=["income"],
            concerning_paths=["age"],
        )
        with mock.patch(
            "orxaq.orchestration.integration.OrxaqEngine.fairness_audit",
            return_value=fake_decomp,
        ):
            rc = main(["credit", "fairness", "--protected", "age"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "via income" in out or "via loan_amount" in out


# ── Lines 700-701: unknown scenario name in credit compare ───────────────────


class TestCreditCompareUnknownScenario:
    """Lines 700-701: unknown scenario → print error, return 1."""

    def test_unknown_scenario_returns_1(self, capsys) -> None:
        rc = main(
            [
                "credit",
                "compare",
                "--scenarios",
                "invalid_scenario_xyz,also_invalid",
            ]
        )
        assert rc == 1
        out = capsys.readouterr().out
        assert "Unknown scenario" in out


# ── Lines 833-836: worst violations in validate dsep ─────────────────────────


class TestValidateDsepWorstViolations:
    """Lines 833-836: worst violations block printed when non-empty."""

    def test_worst_violations_printed(self, capsys) -> None:
        from orxaq.intelligence.validation import DSepImplication

        fake_violation = DSepImplication(
            x="A",
            y="B",
            z_set=frozenset({"C"}),
            dag_implies_independent=True,
            data_independent=False,
            violated=True,
            p_value=0.001,
        )

        fake_report = mock.MagicMock()
        fake_report.passed = False
        fake_report.total_implications = 10
        fake_report.violations = 1
        fake_report.violation_rate = 0.1
        fake_report.worst_violations = [fake_violation]

        with mock.patch(
            "orxaq.orchestration.integration.OrxaqEngine.validate_dag",
            return_value=fake_report,
        ):
            rc = main(["validate"])  # no subcommand: d-sep mode
        assert rc == 0
        out = capsys.readouterr().out
        assert "Worst violations" in out
        assert "A _||_ B" in out


# ── Lines 999, 1001: audit log action/actor filters ──────────────────────────


class TestAuditFilters:
    """Lines 999, 1001: audit entries filtered by action and actor."""

    def test_audit_filter_by_action(self, capsys) -> None:
        rc = main(["audit", "--action", "load.ontology"])
        assert rc == 0

    def test_audit_filter_by_actor(self, capsys) -> None:
        rc = main(["audit", "--actor", "system"])
        assert rc == 0

    def test_audit_filter_by_both(self, capsys) -> None:
        from orxaq.kernel.audit import AuditEntry, AuditLog

        entry = AuditEntry(
            sequence=1,
            timestamp=0.0,
            action="test.action",
            actor="tester",
            details="details",
        )
        with mock.patch.object(AuditLog, "entries", return_value=[entry]):
            rc = main(["audit", "--action", "test.action", "--actor", "tester"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "test.action" in out


# ── Line 1020: audit entries loop display ────────────────────────────────────


class TestAuditEntriesDisplay:
    """Line 1020: entries printed in the for-loop."""

    def test_audit_entries_printed(self, capsys) -> None:
        from orxaq.kernel.audit import AuditEntry, AuditLog

        entries = [
            AuditEntry(
                sequence=1,
                timestamp=0.0,
                action="credit.ecl",
                actor="alice",
                details="ECL computed",
            ),
            AuditEntry(
                sequence=2,
                timestamp=60.0,
                action="credit.pd",
                actor="bob",
                details="PD estimated",
            ),
        ]
        with mock.patch.object(AuditLog, "entries", return_value=entries):
            rc = main(["audit"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "credit.ecl" in out
        assert "alice" in out


# ── Lines 1027-1032: serve command ───────────────────────────────────────────


class TestServeCommand:
    """Lines 1027-1032: `serve` command calls server.serve()."""

    def test_serve_calls_serve(self) -> None:
        with mock.patch("orxaq.experiences.server.serve") as mock_serve:
            rc = main(["serve"])
        assert rc == 0
        mock_serve.assert_called_once()


# ── Lines 1036-1045: edit command ─────────────────────────────────────────────


class TestEditCommand:
    """Lines 1036-1045: `edit` command calls serve() and opens browser."""

    def test_edit_opens_browser(self, capsys) -> None:
        with (
            mock.patch("orxaq.experiences.server.serve"),
            mock.patch("webbrowser.open") as mock_browser,
        ):
            rc = main(["edit"])
        assert rc == 0
        mock_browser.assert_called_once()
        out = capsys.readouterr().out
        assert "Opening DAG Editor" in out


# ── Line 1060: simulate from JSON file ────────────────────────────────────────


class TestSimulateFromFile:
    """Line 1060: scenario loaded from a JSON file (--file flag)."""

    def test_simulate_from_file(self, tmp_path: Path, capsys) -> None:
        from orxaq.intelligence.simulator import BUILT_IN_SCENARIOS

        scenario = list(BUILT_IN_SCENARIOS.values())[0]
        scenario_file = tmp_path / "scenario.json"
        scenario_file.write_text(json.dumps({"name": "adverse", "shocks": {}}))

        with mock.patch(
            "orxaq.intelligence.simulator.Scenario.from_json",
            return_value=scenario,
        ):
            rc = main(["simulate", "--file", str(scenario_file)])
        assert rc == 0


# ── Line 1158: budget WARNING status ──────────────────────────────────────────


class TestBudgetWarningStatus:
    """Line 1158: status string set to 'WARNING (>80%)' when warning flag set."""

    def test_cost_status_shows_warning(self, capsys) -> None:
        from orxaq.orchestration.providers.cost import BudgetManager

        warning_status = {
            "monthly_budget_usd": 500.0,
            "spent_usd": 450.0,
            "remaining_usd": 50.0,
            "daily_budget_usd": 16.67,
            "total_calls": 100,
            "within_budget": True,
            "warning": True,
        }
        with mock.patch.object(BudgetManager, "status", return_value=warning_status):
            rc = main(["cost", "status"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "WARNING" in out


# ── Lines 1198-1201: providers cache not active ───────────────────────────────


class TestProvidersCacheNotActive:
    """Lines 1198-1201: cache returns None → print 'not active', return 0."""

    def test_providers_cache_not_active_text(self, capsys) -> None:
        with mock.patch("orxaq.kernel.cache.get_registry") as mock_reg:
            mock_reg.return_value.get.return_value = None
            rc = main(["providers", "cache"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "not active" in out

    def test_providers_cache_not_active_json(self, capsys) -> None:
        with mock.patch("orxaq.kernel.cache.get_registry") as mock_reg:
            mock_reg.return_value.get.return_value = None
            rc = main(["--json", "providers", "cache"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert data["enabled"] is False


# ── Lines 1241-1248: providers stats with latency and failures ────────────────


class TestProvidersStatsDisplay:
    """Lines 1241-1248: latency and failure tables printed."""

    def test_providers_stats_with_latency_and_failures(self, capsys) -> None:
        fake_stats = {
            "priority": ["openai", "anthropic"],
            "cache": {"enabled": True, "entries": 5, "hit_rate": 0.75},
            "latency": {
                "openai": {
                    "calls": 10,
                    "avg_ms": 250.0,
                    "p50_ms": 220.0,
                    "p95_ms": 400.0,
                }
            },
            "failures": {"openai": 2, "anthropic": 0},
        }
        with mock.patch(
            "orxaq.orchestration.providers.router.ProviderRouter.stats",
            return_value=fake_stats,
        ):
            rc = main(["providers", "stats"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "Latency" in out
        assert "openai" in out.lower()
        assert "Failures" in out


# ── Lines 1449-1451: model regression checkpoint not found ───────────────────


class TestModelRegressionNotFound:
    """Lines 1449-1451: regression returns 1 when checkpoint missing."""

    def test_regression_not_found(self, tmp_path: Path, capsys) -> None:
        rc = main(
            [
                "model",
                "regression",
                "--store",
                str(tmp_path),
                "--name",
                "nonexistent_model",
                "--v1",
                "v0.1.0",
                "--v2",
                "v0.2.0",
            ]
        )
        assert rc == 1
        out = capsys.readouterr().out
        assert "not found" in out.lower() or "Checkpoint" in out


# ── Lines 1492-1494: model metrics checkpoint not found ──────────────────────


class TestModelMetricsNotFound:
    """Lines 1492-1494: metrics returns 1 when checkpoint missing."""

    def test_model_metrics_not_found(self, tmp_path: Path, capsys) -> None:
        rc = main(
            [
                "model",
                "metrics",
                "--store",
                str(tmp_path),
                "--name",
                "noexist_model",
            ]
        )
        assert rc == 1
        out = capsys.readouterr().out
        assert "not found" in out.lower() or "Checkpoint" in out


# ── Lines 1691-1692: batch run with no job_type ───────────────────────────────


class TestBatchRunNoJobType:
    """Lines 1691-1692: batch run without specifying job_type returns error.

    argparse enforces job_type as required, so we must call _cmd_batch_run
    directly with a fake namespace to cover the runtime guard."""

    def test_batch_run_no_job_type(self, capsys) -> None:
        from orxaq.experiences.cli import _cmd_batch_run

        fake_args = argparse.Namespace(job_type=None, params=None, json=False)
        rc = _cmd_batch_run(fake_args, mock.MagicMock())
        assert rc == 1
        out = capsys.readouterr().out
        assert "job type required" in out.lower() or "Error" in out


# ── Line 1696: batch run with params JSON ─────────────────────────────────────


class TestBatchRunWithParams:
    """Line 1696: params JSON parsed from --params flag."""

    def test_batch_run_with_params(self, capsys) -> None:
        # job_type is a positional arg with choices ecl|discover|fairness
        rc = main(
            [
                "batch",
                "run",
                "ecl",
                "--params",
                '{"n": 50, "seed": 42}',
            ]
        )
        assert rc == 0
        out = capsys.readouterr().out
        assert "completed" in out.lower() or "Job" in out


# ── Line 1739: batch stats by-status loop ────────────────────────────────────


class TestBatchStatsByStatus:
    """Line 1739: batch stats prints by-status entries."""

    def test_batch_stats_text(self, capsys) -> None:
        rc = main(["batch", "stats"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "Batch Queue Stats" in out


# ── Lines 1957-1960: learn compare unchanged metrics ─────────────────────────


class TestLearnCompareUnchangedMetrics:
    """Lines 1957-1960: 'Unchanged' section printed in learn compare."""

    def test_learn_compare_unchanged(self, tmp_path: Path, capsys) -> None:
        from orxaq.intelligence.learning.improvement import ImprovementDetector
        from orxaq.intelligence.learning.metrics import DiscoveryMetrics, MetricCollector

        collector = MetricCollector()
        store_path = str(tmp_path / "metrics.json")

        run_a = DiscoveryMetrics(
            run_id="run-001",
            algorithm="pc",
            shd=2,
            edge_f1=0.8,
            edge_precision=0.85,
            edge_recall=0.75,
            orientation_accuracy=0.9,
            execution_time_ms=100.0,
        )
        run_b = DiscoveryMetrics(
            run_id="run-002",
            algorithm="pc",
            shd=2,
            edge_f1=0.8,
            edge_precision=0.85,
            edge_recall=0.75,
            orientation_accuracy=0.9,
            execution_time_ms=100.0,
        )
        collector.record(run_a)
        collector.record(run_b)
        # Serialize runs since MetricCollector has no save()
        runs_data = [dataclasses.asdict(r) for r in collector.runs]
        Path(store_path).write_text(json.dumps(runs_data))

        fake_summary = {
            "improved": [],
            "regressed": [],
            "unchanged": [{"metric": "edge_f1", "previous": 0.8, "current": 0.8, "delta": 0.0}],
            "net_improvements": 0,
        }
        with mock.patch.object(
            ImprovementDetector, "improvement_summary", return_value=fake_summary
        ):
            rc = main(
                [
                    "learn",
                    "compare",
                    "--store",
                    store_path,
                    "--run-a",
                    "run-001",
                    "--run-b",
                    "run-002",
                ]
            )
        assert rc == 0
        out = capsys.readouterr().out
        assert "Unchanged" in out


# ── Lines 1978-1987: cost summary with recorded usage ────────────────────────


class TestCostSummaryWithUsage:
    """Lines 1978-1987: cost summary printed when total_calls > 0."""

    def test_cost_summary_with_usage(self, capsys) -> None:
        from orxaq.orchestration.providers.cost import CostTracker

        fake_summary = {
            "total_cost_usd": 0.0523,
            "total_calls": 42,
            "total_input_tokens": 10000,
            "total_output_tokens": 5000,
            "by_provider": {
                "openai": {"cost_usd": 0.0523, "calls": 42},
            },
        }
        with mock.patch.object(CostTracker, "summary", return_value=fake_summary):
            rc = main(["cost", "summary"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "Cost Summary" in out
        assert "By provider" in out
        assert "openai" in out.lower()


# ── Line 2111: db get returns JSON for missing key ────────────────────────────


class TestDbGetMissingJson:
    """Line 2111: db get with --json when key not found → JSON {value: null}, return 0."""

    def test_db_get_missing_key_json(self, tmp_path: Path, capsys) -> None:
        db_path = str(tmp_path / "test.db")
        rc = main(["--json", "db", "--db", db_path, "get", "nonexistent_key_xyz"])
        # In JSON mode, _json_out returns 0 even for null values (line 2111)
        assert rc == 0
        out = capsys.readouterr().out
        data = json.loads(out)
        assert data["value"] is None
        assert data["key"] == "nonexistent_key_xyz"


# ── Lines 2311-2314: stream ECL pagination ───────────────────────────────────


class TestStreamECLPagination:
    """Lines 2311-2314: stream ecl with --page > 0 uses paginate()."""

    def test_stream_ecl_paginated_text(self, capsys) -> None:
        rc = main(
            [
                "stream",
                "ecl",
                "--page",
                "1",
                "--page-size",
                "5",
            ]
        )
        assert rc == 0
        out = capsys.readouterr().out
        assert "Page" in out or len(out) > 0


# ── Line 2382: audit-report full with >20 entries ────────────────────────────


class TestAuditReportFullManyEntries:
    """Line 2382: '... and N more' printed when entry_count > 20."""

    def test_audit_report_full_many_entries(self, capsys) -> None:
        from orxaq.fabric.audit_report import AuditReportGenerator

        fake_entry = {
            "iso_time": "2026-01-01T00:00:00",
            "action": "test.action",
            "actor": "system",
            "details": "details",
        }
        fake_summary = mock.MagicMock()
        fake_summary.total_events = 25
        fake_summary.unique_actors = 1
        fake_summary.unique_actions = 1

        fake_report = mock.MagicMock()
        fake_report.title = "Test Report"
        fake_report.summary = fake_summary
        fake_report.integrity = None
        fake_report.entries = [fake_entry] * 25
        fake_report.entry_count = 25

        with mock.patch.object(AuditReportGenerator, "full_report", return_value=fake_report):
            rc = main(["audit-report", "full"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "more" in out


# ── Lines 2395-2401: audit-report compliance text ────────────────────────────


class TestAuditReportComplianceText:
    """Lines 2395-2401: compliance report printed in text mode."""

    def test_audit_report_compliance_text(self, capsys) -> None:
        from orxaq.fabric.audit_report import AuditReportGenerator

        fake_summary = mock.MagicMock()
        fake_summary.total_events = 5
        fake_summary.unique_actors = 2

        fake_report = mock.MagicMock()
        fake_report.title = "SR 11-7 Compliance Report"
        fake_report.summary = fake_summary
        fake_report.integrity = None

        with mock.patch.object(AuditReportGenerator, "compliance_report", return_value=fake_report):
            rc = main(["audit-report", "compliance"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "SR 11-7 Compliance Report" in out or "Events" in out


# ── Line 2417: audit-report verify with invalid chain ────────────────────────


class TestAuditReportVerifyInvalid:
    """Line 2417: error message printed when integrity invalid."""

    def test_audit_report_verify_invalid(self, capsys) -> None:
        from orxaq.fabric.audit_report import AuditReportGenerator, IntegrityResult

        fake_result = mock.MagicMock(spec=IntegrityResult)
        fake_result.valid = False
        fake_result.total_entries = 10
        fake_result.checked_entries = 10
        fake_result.error_message = "Hash mismatch at entry 5"

        with mock.patch.object(AuditReportGenerator, "verify_integrity", return_value=fake_result):
            rc = main(["audit-report", "verify"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "INVALID" in out
        assert "Hash mismatch" in out


# ── Lines 2449-2450: bulk run without --input ─────────────────────────────────


class TestBulkRunNoInput:
    """Lines 2449-2450: bulk run without --input → error, return 1.

    argparse marks --input as required, so we call _cmd_bulk_run directly."""

    def test_bulk_run_no_input_returns_1(self, capsys) -> None:
        from orxaq.experiences.cli import _cmd_bulk_run

        fake_args = argparse.Namespace(input=None, json=False)
        rc = _cmd_bulk_run(fake_args, mock.MagicMock())
        assert rc == 1
        out = capsys.readouterr().out
        assert "required" in out.lower() or "Error" in out


# ── Lines 2478-2479: bulk run with processing errors ─────────────────────────


class TestBulkRunWithErrors:
    """Lines 2478-2479: bulk run errors printed to stderr."""

    def test_bulk_run_errors_printed(self, tmp_path: Path, capsys) -> None:
        input_file = tmp_path / "input.jsonl"
        input_file.write_text('{"x": 1}\n{"y": 2}\n')

        from orxaq.orchestration.bulk import BulkProcessor, BulkProgress

        fake_progress = BulkProgress(processed=2, succeeded=1, failed=1)
        fake_result = mock.MagicMock()
        fake_result.progress = fake_progress
        fake_result.success_rate = 0.5
        fake_result.errors = [{"index": 1, "error": "test error msg"}]
        fake_result.to_dict.return_value = {}

        with mock.patch.object(BulkProcessor, "run", return_value=fake_result):
            rc = main(["bulk", "run", "--input", str(input_file)])
        assert rc == 0
        captured = capsys.readouterr()
        assert "test error msg" in captured.err or "Error" in captured.err


# ── Lines 2491-2492: bulk dry-run without --input ────────────────────────────


class TestBulkDryRunNoInput:
    """Lines 2491-2492: bulk dry-run without --input → error, return 1.

    argparse marks --input as required, so we call _cmd_bulk_dry_run directly."""

    def test_bulk_dry_run_no_input_returns_1(self, capsys) -> None:
        from orxaq.experiences.cli import _cmd_bulk_dry_run

        fake_args = argparse.Namespace(input=None, json=False)
        rc = _cmd_bulk_dry_run(fake_args, mock.MagicMock())
        assert rc == 1
        out = capsys.readouterr().out
        assert "required" in out.lower() or "Error" in out


# ── Lines 2536-2538: filing run raises exception ─────────────────────────────


class TestFilingRunException:
    """Lines 2536-2538: pipeline exception prints error, returns 1.

    The 'run' command is top-level (orxaq run), not a filing subcommand."""

    def test_filing_run_exception(self, tmp_path: Path, capsys) -> None:
        with mock.patch(
            "orxaq.orchestration.run.run_pipeline",
            side_effect=RuntimeError("pipeline exploded"),
        ):
            rc = main(
                [
                    "run",
                    "--institution",
                    "TEST_BANK",
                    "--output",
                    str(tmp_path),
                ]
            )
        assert rc == 1


# ── Lines 2632-2634: demo no/wrong subcommand ────────────────────────────────


class TestDemoNoSubcommand:
    """Lines 2632-2634: demo without 'regulatory' subcommand → usage, return 1."""

    def test_demo_no_subcommand_returns_1(self, capsys) -> None:
        rc = main(["demo"])
        assert rc == 1
        out = capsys.readouterr().out
        assert "Usage" in out or "demo regulatory" in out


# ── Lines 3450-3452: tenant create ValueError ────────────────────────────────


class TestTenantCreateDuplicate:
    """Lines 3450-3452: tenant create raises ValueError → error message, return 1."""

    def test_tenant_create_duplicate_returns_1(self, capsys) -> None:
        from orxaq.enterprise.tenant import TenantStore

        with mock.patch.object(
            TenantStore, "create", side_effect=ValueError("tenant already exists")
        ):
            rc = main(
                [
                    "tenant",
                    "create",
                    "duplicate-tenant",
                    "--name",
                    "Dupe Bank",
                ]
            )
        assert rc == 1
        out = capsys.readouterr().out
        assert "Error" in out or "already exists" in out
