"""Tests for CLI entry point."""

from __future__ import annotations

from orxaq.experiences.cli import main


class TestCLIStatus:
    def test_status_exits_zero(self, capsys):
        rc = main(["status"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Orxaq Status" in captured.out


class TestCLICreditOntology:
    def test_ontology_show(self, capsys):
        rc = main(["credit", "ontology", "--show"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "19 variables" in captured.out
        assert "credit_score" in captured.out


class TestCLICreditECL:
    def test_ecl_synthetic(self, capsys):
        rc = main(["credit", "ecl"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "CECL Expected Credit Loss" in captured.out
        assert "Total ECL" in captured.out


class TestCLICreditFairness:
    def test_fairness_age(self, capsys):
        rc = main(["credit", "fairness", "--protected", "age"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Fair Lending Analysis" in captured.out
        assert "age" in captured.out


class TestCLIDiscover:
    def test_discover_consensus(self, capsys):
        rc = main(
            ["discover", "--consensus", "--variables", "unemployment_rate,income,credit_score"]
        )
        assert rc == 0
        captured = capsys.readouterr()
        assert "Discovery" in captured.out

    def test_discover_pc(self, capsys):
        rc = main(["discover", "--variables", "unemployment_rate,income,credit_score"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Discovery" in captured.out


class TestCLIValidate:
    def test_validate(self, capsys):
        rc = main(["validate"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "D-Separation Validation" in captured.out

    def test_validate_generate(self, capsys, tmp_path):
        out = str(tmp_path / "gen.csv")
        rc = main(["validate", "--generate", "--rows", "100", "--seed", "1", "--output", out])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Generated 100 rows" in captured.out

    def test_validate_pipeline(self, capsys):
        gt = "data/validation/ground_truth_dag.json"
        rc = main(["validate", "--ground-truth", gt, "--algorithm", "pc"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Validation Pipeline Report" in captured.out

    def test_validate_pipeline_json(self, capsys):
        gt = "data/validation/ground_truth_dag.json"
        rc = main(["--json", "validate", "--ground-truth", gt, "--algorithm", "pc"])
        assert rc == 0
        import json

        data = json.loads(capsys.readouterr().out)
        assert "metrics" in data
        assert "algorithm" in data


class TestCLIAudit:
    def test_audit_empty(self, capsys):
        rc = main(["audit", "--last", "5"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Audit Log" in captured.out


class TestCLIFabricQuality:
    def test_quality_synthetic(self, capsys):
        rc = main(["fabric", "quality"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Quality Gates" in captured.out


class TestCLIJsonMode:
    def test_status_json(self, capsys):
        rc = main(["--json", "status"])
        assert rc == 0
        import json

        data = json.loads(capsys.readouterr().out)
        assert "dag" in data
        assert "audit_entries" in data

    def test_ecl_json(self, capsys):
        rc = main(["--json", "credit", "ecl"])
        assert rc == 0
        import json

        data = json.loads(capsys.readouterr().out)
        assert "total_ecl" in data
        assert data["total_ecl"] >= 0

    def test_validate_json(self, capsys):
        rc = main(["--json", "validate"])
        assert rc == 0
        import json

        data = json.loads(capsys.readouterr().out)
        assert "total_implications" in data
        assert "passed" in data

    def test_audit_json(self, capsys):
        rc = main(["--json", "audit"])
        assert rc == 0
        import json

        data = json.loads(capsys.readouterr().out)
        assert "entries" in data


class TestCLILearn:
    """Tests for the `orxaq learn` CLI subcommands."""

    def _write_metrics_json(self, path, runs):
        """Helper to write metrics JSON store file."""
        import json

        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(json.dumps(runs, indent=2))

    def _write_experiments_json(self, path, experiments):
        """Helper to write experiments JSON store file."""
        import json

        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(json.dumps(experiments, indent=2))

    def test_learn_summary_empty(self, capsys, tmp_path):
        store = tmp_path / "metrics.json"
        rc = main(["learn", "summary", "--store", str(store)])
        assert rc == 0
        captured = capsys.readouterr()
        assert "no runs recorded" in captured.out

    def test_learn_summary_with_data(self, capsys, tmp_path):
        store = tmp_path / "metrics.json"
        self._write_metrics_json(
            store,
            [
                {
                    "run_id": "r1",
                    "algorithm": "pc",
                    "shd": 3,
                    "edge_f1": 0.85,
                    "edge_precision": 0.9,
                    "edge_recall": 0.8,
                    "orientation_accuracy": 0.75,
                    "execution_time_ms": 50.0,
                    "timestamp": 1000.0,
                    "dataset_size": 1000,
                    "num_variables": 5,
                    "config": {},
                }
            ],
        )
        rc = main(["learn", "summary", "--store", str(store)])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Learning Metrics Summary" in captured.out
        assert "Total runs" in captured.out

    def test_learn_summary_json(self, capsys, tmp_path):
        import json

        store = tmp_path / "metrics.json"
        rc = main(["--json", "learn", "summary", "--store", str(store)])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "total_runs" in data
        assert data["total_runs"] == 0

    def test_learn_experiments_empty(self, capsys, tmp_path):
        store = tmp_path / "experiments.json"
        rc = main(["learn", "experiments", "--store", str(store)])
        assert rc == 0
        captured = capsys.readouterr()
        assert "No experiments" in captured.out

    def test_learn_experiments_with_data(self, capsys, tmp_path):
        store = tmp_path / "experiments.json"
        self._write_experiments_json(
            store,
            {
                "exp1": {
                    "experiment_id": "exp1",
                    "name": "test_exp",
                    "algorithm": "pc",
                    "algorithm_config": {},
                    "data_config": {},
                    "results": {},
                    "start_time": 1000.0,
                    "end_time": 1001.0,
                    "status": "completed",
                }
            },
        )
        rc = main(["learn", "experiments", "--store", str(store)])
        assert rc == 0
        captured = capsys.readouterr()
        assert "exp1" in captured.out

    def test_learn_best_empty(self, capsys, tmp_path):
        store = tmp_path / "metrics.json"
        rc = main(["learn", "best", "--store", str(store)])
        assert rc == 0
        captured = capsys.readouterr()
        assert "No runs recorded" in captured.out

    def test_learn_compare_missing_args(self, capsys, tmp_path):
        store = tmp_path / "metrics.json"
        rc = main(["learn", "compare", "--store", str(store)])
        assert rc == 1
        captured = capsys.readouterr()
        assert "--run-a" in captured.out

    def test_learn_compare_missing_run(self, capsys, tmp_path):
        store = tmp_path / "metrics.json"
        self._write_metrics_json(store, [])
        rc = main(["learn", "compare", "--store", str(store), "--run-a", "r1", "--run-b", "r2"])
        assert rc == 1
        captured = capsys.readouterr()
        assert "not found" in captured.out

    def test_learn_compare_with_data(self, capsys, tmp_path):
        store = tmp_path / "metrics.json"
        self._write_metrics_json(
            store,
            [
                {
                    "run_id": "r1",
                    "algorithm": "pc",
                    "shd": 5,
                    "edge_f1": 0.7,
                    "edge_precision": 0.8,
                    "edge_recall": 0.65,
                    "orientation_accuracy": 0.7,
                    "execution_time_ms": 100.0,
                    "timestamp": 1000.0,
                    "dataset_size": 1000,
                    "num_variables": 5,
                    "config": {},
                },
                {
                    "run_id": "r2",
                    "algorithm": "pc",
                    "shd": 2,
                    "edge_f1": 0.9,
                    "edge_precision": 0.9,
                    "edge_recall": 0.9,
                    "orientation_accuracy": 0.85,
                    "execution_time_ms": 80.0,
                    "timestamp": 1001.0,
                    "dataset_size": 1000,
                    "num_variables": 5,
                    "config": {},
                },
            ],
        )
        rc = main(["learn", "compare", "--store", str(store), "--run-a", "r1", "--run-b", "r2"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Comparison" in captured.out
        assert "r1" in captured.out
        assert "r2" in captured.out

    def test_learn_no_subcommand(self, capsys):
        try:
            rc = main(["learn"])
            assert rc == 1
        except SystemExit as e:
            assert e.code in (0, 1, 2)


class TestCLIProviders:
    def test_providers_cache_human(self, capsys):
        rc = main(["providers", "cache"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Cache" in captured.out

    def test_providers_cache_json(self, capsys):
        import json

        rc = main(["--json", "providers", "cache"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "entries" in data
        assert "hits" in data

    def test_providers_stats_human(self, capsys):
        rc = main(["providers", "stats"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Routing Statistics" in captured.out
        assert "Priority" in captured.out

    def test_providers_stats_json(self, capsys):
        import json

        rc = main(["--json", "providers", "stats"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "cache" in data
        assert "priority" in data
        assert data["cache"]["enabled"] is True

    def test_providers_check(self, capsys):
        rc = main(["providers", "check"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Health Check" in captured.out

    def test_providers_list(self, capsys):
        rc = main(["providers", "list"])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Available Models" in captured.out


class TestCLIModel:
    def test_model_save_and_load(self, capsys, tmp_path):
        store = str(tmp_path / "checkpoints")
        rc = main(["model", "save", "--name", "test_model", "--version", "1.0.0", "--store", store])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Saved checkpoint" in captured.out
        assert "test_model" in captured.out

        rc = main(["model", "load", "--name", "test_model", "--version", "1.0.0", "--store", store])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Loaded checkpoint" in captured.out

    def test_model_save_json(self, capsys, tmp_path):
        import json

        store = str(tmp_path / "checkpoints")
        rc = main(["--json", "model", "save", "--name", "jm", "--version", "1.0", "--store", store])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert data["saved"] is True
        assert data["name"] == "jm"
        assert "content_hash" in data

    def test_model_list_empty(self, capsys, tmp_path):
        store = str(tmp_path / "checkpoints")
        rc = main(["model", "list", "--store", store])
        assert rc == 0
        captured = capsys.readouterr()
        assert "No saved models" in captured.out

    def test_model_list_with_data(self, capsys, tmp_path):
        store = str(tmp_path / "checkpoints")
        main(["model", "save", "--name", "alpha", "--version", "1.0", "--store", store])
        capsys.readouterr()
        rc = main(["model", "list", "--store", store])
        assert rc == 0
        captured = capsys.readouterr()
        assert "alpha" in captured.out
        assert "1 version" in captured.out

    def test_model_list_json(self, capsys, tmp_path):
        import json

        store = str(tmp_path / "checkpoints")
        main(["model", "save", "--name", "beta", "--version", "1.0", "--store", store])
        capsys.readouterr()
        rc = main(["--json", "model", "list", "--store", store])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert len(data["models"]) == 1
        assert data["models"][0]["name"] == "beta"

    def test_model_versions(self, capsys, tmp_path):
        store = str(tmp_path / "checkpoints")
        main(["model", "save", "--name", "m", "--version", "1.0", "--store", store])
        main(["model", "save", "--name", "m", "--version", "2.0", "--store", store])
        capsys.readouterr()
        rc = main(["model", "versions", "--name", "m", "--store", store])
        assert rc == 0
        captured = capsys.readouterr()
        assert "2.0" in captured.out
        assert "1.0" in captured.out

    def test_model_diff(self, capsys, tmp_path):
        store = str(tmp_path / "checkpoints")
        main(["model", "save", "--name", "d", "--version", "1.0", "--store", store])
        main(["model", "save", "--name", "d", "--version", "2.0", "--store", store])
        capsys.readouterr()
        rc = main(["model", "diff", "--name", "d", "--v1", "1.0", "--v2", "2.0", "--store", store])
        assert rc == 0
        captured = capsys.readouterr()
        assert "Diff" in captured.out

    def test_model_diff_json(self, capsys, tmp_path):
        import json

        store = str(tmp_path / "checkpoints")
        main(["model", "save", "--name", "dj", "--version", "1.0", "--store", store])
        main(["model", "save", "--name", "dj", "--version", "2.0", "--store", store])
        capsys.readouterr()
        rc = main(
            [
                "--json",
                "model",
                "diff",
                "--name",
                "dj",
                "--v1",
                "1.0",
                "--v2",
                "2.0",
                "--store",
                store,
            ]
        )
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert data["identical"] is True  # Same DAG, same content
        assert data["v1"] == "1.0"
        assert data["v2"] == "2.0"

    def test_model_load_latest(self, capsys, tmp_path):
        store = str(tmp_path / "checkpoints")
        main(["model", "save", "--name", "lat", "--version", "1.0", "--store", store])
        main(["model", "save", "--name", "lat", "--version", "2.0", "--store", store])
        capsys.readouterr()
        rc = main(["model", "load", "--name", "lat", "--store", store])
        assert rc == 0
        captured = capsys.readouterr()
        assert "2.0" in captured.out

    def test_model_load_nonexistent(self, capsys, tmp_path):
        store = str(tmp_path / "checkpoints")
        rc = main(["model", "load", "--name", "missing", "--store", store])
        assert rc == 1


class TestCLIExport:
    def test_export_dag_json(self, capsys):
        import json

        rc = main(["--json", "export", "dag"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "nodes" in data
        assert "edges" in data

    def test_export_dag_csv(self, capsys):
        rc = main(["export", "dag", "--format", "csv"])
        assert rc == 0
        # CSV may be empty if default DAG has no edges
        capsys.readouterr()

    def test_export_dag_dot(self, capsys):
        rc = main(["export", "dag", "--format", "dot"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "digraph" in out

    def test_export_dag_mermaid(self, capsys):
        rc = main(["export", "dag", "--format", "mermaid"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "graph TD" in out

    def test_export_dag_to_file(self, capsys, tmp_path):
        outfile = str(tmp_path / "dag.json")
        rc = main(["export", "dag", "--output", outfile])
        assert rc == 0
        import json
        from pathlib import Path

        content = Path(outfile).read_text()
        data = json.loads(content)
        assert "nodes" in data

    def test_export_ecl_json(self, capsys):
        import json

        rc = main(["--json", "export", "ecl"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "records" in data

    def test_export_ecl_csv(self, capsys):
        rc = main(["export", "ecl", "--format", "csv"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "segment" in out

    def test_export_ecl_to_file(self, capsys, tmp_path):
        outfile = str(tmp_path / "ecl.csv")
        rc = main(["export", "ecl", "--format", "csv", "--output", outfile])
        assert rc == 0
        from pathlib import Path

        assert Path(outfile).exists()

    def test_export_edges(self, capsys):
        rc = main(["export", "edges"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "source" in out
        assert "target" in out

    def test_export_edges_to_file(self, capsys, tmp_path):
        outfile = str(tmp_path / "edges.csv")
        rc = main(["export", "edges", "--output", outfile])
        assert rc == 0

    def test_export_nodes(self, capsys):
        rc = main(["export", "nodes"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "entity_id" in out
        assert "name" in out

    def test_export_nodes_to_file(self, capsys, tmp_path):
        outfile = str(tmp_path / "nodes.csv")
        rc = main(["export", "nodes", "--output", outfile])
        assert rc == 0

    def test_export_dag_json_to_file(self, capsys, tmp_path):
        import json

        outfile = str(tmp_path / "dag_out.json")
        rc = main(["--json", "export", "dag", "--output", outfile])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert data["exported"] is True


class TestCLIBatch:
    """Tests for the `orxaq batch` CLI subcommands."""

    def _reset_queue(self):
        """Reset singleton batch queue."""
        import orxaq.experiences.cli as cli_mod

        cli_mod._batch_queue = None

    def test_batch_run_ecl(self, capsys):
        self._reset_queue()
        rc = main(["batch", "run", "ecl"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "completed" in out

    def test_batch_run_ecl_json(self, capsys):
        import json

        self._reset_queue()
        rc = main(["--json", "batch", "run", "ecl"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert data["status"] == "completed"
        assert "total_ecl" in data.get("result", {})

    def test_batch_run_fairness(self, capsys):
        self._reset_queue()
        rc = main(["batch", "run", "fairness"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "completed" in out

    def test_batch_list_empty(self, capsys):
        self._reset_queue()
        rc = main(["batch", "list"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "No batch jobs" in out

    def test_batch_list_after_run(self, capsys):
        self._reset_queue()
        main(["batch", "run", "ecl"])
        capsys.readouterr()
        rc = main(["batch", "list"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "Batch Jobs" in out

    def test_batch_list_json(self, capsys):
        import json

        self._reset_queue()
        rc = main(["--json", "batch", "list"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "jobs" in data

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

    def test_batch_stats_json(self, capsys):
        import json

        self._reset_queue()
        rc = main(["--json", "batch", "stats"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "total_jobs" in data

    def test_batch_clear(self, capsys):
        self._reset_queue()
        main(["batch", "run", "ecl"])
        capsys.readouterr()
        rc = main(["batch", "clear"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "Cleared" in out

    def test_batch_clear_json(self, capsys):
        import json

        self._reset_queue()
        main(["batch", "run", "ecl"])
        capsys.readouterr()
        rc = main(["--json", "batch", "clear"])
        assert rc == 0
        data = json.loads(capsys.readouterr().out)
        assert "cleared" in data


class TestCLIErrors:
    def test_no_command(self, capsys):
        rc = main([])
        assert rc == 1

    def test_invalid_credit_subcommand(self, capsys):
        # Missing subcommand should show help and exit 1
        try:
            rc = main(["credit"])
            # If it doesn't raise, check return code
            assert rc == 1
        except SystemExit as e:
            assert e.code in (0, 1, 2)


class TestCLIRegulatoryExport:
    def test_regulatory_export_json(self, capsys):
        import json as _json

        rc = main(
            [
                "credit",
                "regulatory-export",
                "--institution-id",
                "TEST001",
                "--as-of",
                "2025-12-31",
                "--period",
                "2025Q4",
                "--format",
                "json",
            ]
        )
        assert rc == 0
        captured = capsys.readouterr()
        data = _json.loads(captured.out)
        assert "schedule_h1" in data
        assert "ccar_projection" in data

    def test_regulatory_export_csv(self, capsys):
        rc = main(
            [
                "credit",
                "regulatory-export",
                "--format",
                "csv",
            ]
        )
        assert rc == 0
        captured = capsys.readouterr()
        # CSV header should contain segment column
        assert "segment" in captured.out

    def test_regulatory_export_to_file(self, tmp_path):
        import json as _json

        out_file = str(tmp_path / "regulatory.json")
        rc = main(
            [
                "credit",
                "regulatory-export",
                "--institution-id",
                "BANK01",
                "--as-of",
                "2025-12-31",
                "--period",
                "2025Q4",
                "--output",
                out_file,
            ]
        )
        assert rc == 0
        from pathlib import Path

        assert Path(out_file).exists()
        text = Path(out_file).read_text()
        data = _json.loads(text)
        assert "ccar_projection" in data or "schedule_h1" in data


class TestCLIMetrics:
    """Tests for the metrics subcommand."""

    def test_metrics_snapshot(self, capsys):
        rc = main(["metrics", "snapshot"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "Metrics Snapshot" in out

    def test_metrics_snapshot_json(self, capsys):
        import json as _j

        rc = main(["--json", "metrics", "snapshot"])
        assert rc == 0
        data = _j.loads(capsys.readouterr().out)
        assert "total_tasks" in data
        assert "uptime_seconds" in data

    def test_metrics_prometheus(self, capsys):
        rc = main(["metrics", "prometheus"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "orxaq_tasks_total" in out
        assert "# HELP" in out

    def test_metrics_prometheus_json(self, capsys):
        import json as _j

        rc = main(["--json", "metrics", "prometheus"])
        assert rc == 0
        data = _j.loads(capsys.readouterr().out)
        assert data["format"] == "prometheus"
        assert "body" in data

    def test_metrics_health(self, capsys):
        rc = main(["metrics", "health"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "Health:" in out

    def test_metrics_health_json(self, capsys):
        import json as _j

        rc = main(["--json", "metrics", "health"])
        assert rc == 0
        data = _j.loads(capsys.readouterr().out)
        assert data["status"] in ("healthy", "degraded", "unhealthy")
        assert "checks" in data

    def test_metrics_recent(self, capsys):
        rc = main(["metrics", "recent"])
        assert rc == 0
        out = capsys.readouterr().out
        assert "No recent tasks" in out

    def test_metrics_recent_json(self, capsys):
        import json as _j

        rc = main(["--json", "metrics", "recent"])
        assert rc == 0
        data = _j.loads(capsys.readouterr().out)
        assert "tasks" in data
        assert data["count"] == 0
