"""Tests for appxen workflow commands."""

from unittest.mock import MagicMock, patch

from typer.testing import CliRunner

from appxen_cli.main import app


class TestWorkflowList:

    @patch("appxen_cli.main.get_client")
    def test_list_empty(self, mock_get_client):
        """List shows 'no workflows' when empty."""
        mock_client = MagicMock()
        mock_client.list_workflows.return_value = {"workflows": []}
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "list"])
        assert result.exit_code == 0
        assert "No workflows found" in result.output

    @patch("appxen_cli.main.get_client")
    def test_list_with_workflows(self, mock_get_client):
        """List displays workflow table."""
        mock_client = MagicMock()
        mock_client.list_workflows.return_value = {
            "workflows": [
                {
                    "workflow_id": "wf_abc123",
                    "name": "churn-analysis",
                    "version": 1,
                    "status": "active",
                    "updated_at": "2026-02-26T12:00:00Z",
                },
            ]
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "list"])
        assert result.exit_code == 0
        assert "churn-analysis" in result.output
        assert "wf_abc123" in result.output

    @patch("appxen_cli.main.get_client")
    def test_list_json(self, mock_get_client):
        """List with --json outputs raw JSON."""
        mock_client = MagicMock()
        mock_client.list_workflows.return_value = {"workflows": []}
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "list", "--json"])
        assert result.exit_code == 0
        assert '"workflows"' in result.output

    def test_list_not_logged_in(self, monkeypatch, tmp_path):
        """List fails when not logged in."""
        monkeypatch.delenv("APPXEN_API_KEY", raising=False)
        import appxen_cli.config as cfg
        monkeypatch.setattr(cfg, "CONFIG_FILE", tmp_path / "nonexistent.toml")

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "list"])
        assert result.exit_code == 1
        assert "Not logged in" in result.output


class TestWorkflowGet:

    @patch("appxen_cli.main.get_client")
    def test_get_workflow(self, mock_get_client):
        """Get shows workflow details."""
        mock_client = MagicMock()
        mock_client.get_workflow.return_value = {
            "workflow_id": "wf_abc",
            "name": "test-wf",
            "version": 1,
            "status": "active",
            "created_at": "2026-02-26T12:00:00Z",
            "updated_at": "2026-02-26T12:00:00Z",
            "plan": {
                "agents": [{"id": "a1"}],
                "steps": [{"id": "s1"}, {"id": "s2"}],
            },
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "get", "wf_abc"])
        assert result.exit_code == 0
        assert "wf_abc" in result.output
        assert "test-wf" in result.output

    @patch("appxen_cli.main.get_client")
    def test_get_workflow_json(self, mock_get_client):
        """Get with --json outputs raw JSON."""
        mock_client = MagicMock()
        mock_client.get_workflow.return_value = {
            "workflow_id": "wf_abc",
            "name": "test",
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "get", "wf_abc", "--json"])
        assert result.exit_code == 0
        assert '"workflow_id"' in result.output

    @patch("appxen_cli.main.get_client")
    def test_get_workflow_error(self, mock_get_client):
        """Get handles API error."""
        mock_client = MagicMock()
        mock_client.get_workflow.side_effect = Exception("Not found")
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "get", "wf_bad"])
        assert result.exit_code == 1
        assert "Failed to get workflow" in result.output


class TestWorkflowDelete:

    @patch("appxen_cli.main.get_client")
    def test_delete_confirmed(self, mock_get_client):
        """Delete with --yes skips confirmation."""
        mock_client = MagicMock()
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "delete", "wf_abc", "--yes"])
        assert result.exit_code == 0
        assert "deleted" in result.output
        mock_client.delete_workflow.assert_called_once_with("wf_abc")

    @patch("appxen_cli.main.get_client")
    def test_delete_cancelled(self, mock_get_client):
        """Delete without --yes prompts and can be cancelled."""
        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "delete", "wf_abc"], input="n\n")
        assert result.exit_code == 0 or result.exit_code is None


class TestWorkflowCompile:

    @patch("appxen_cli.main.get_client")
    def test_compile(self, mock_get_client, tmp_path):
        """Compile reads file and displays plan."""
        md_file = tmp_path / "test.md"
        md_file.write_text("# Test Workflow\n## Steps\n### Step One\nDo it.")

        mock_client = MagicMock()
        mock_client.compile_workflow.return_value = {
            "plan": {
                "name": "Test Workflow",
                "description": "A test",
                "agents": [{"id": "a1", "model": "sonnet", "tools": []}],
                "steps": [
                    {"id": "s1", "agent": "a1", "pattern": "sequential", "depends_on": []}
                ],
            },
            "notes": ["All good"],
            "usage": {"input_tokens": 100, "output_tokens": 50},
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "compile", str(md_file)])
        assert result.exit_code == 0
        assert "Test Workflow" in result.output

    @patch("appxen_cli.main.get_client")
    def test_compile_json(self, mock_get_client, tmp_path):
        """Compile with --json outputs raw JSON."""
        md_file = tmp_path / "test.md"
        md_file.write_text("# Test\n")

        mock_client = MagicMock()
        mock_client.compile_workflow.return_value = {
            "plan": {"name": "Test"},
            "notes": [],
            "usage": {},
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "compile", str(md_file), "--json"])
        assert result.exit_code == 0
        assert '"plan"' in result.output


class TestWorkflowCreate:

    @patch("appxen_cli.main.get_client")
    def test_create(self, mock_get_client, tmp_path):
        """Create compiles and saves workflow."""
        md_file = tmp_path / "my-workflow.md"
        md_file.write_text("# My Workflow\n")

        mock_client = MagicMock()
        mock_client.create_workflow.return_value = {
            "workflow_id": "wf_new123",
            "plan": {
                "name": "My Workflow",
                "description": "",
                "agents": [],
                "steps": [],
            },
            "notes": [],
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "create", str(md_file), "--name", "test-wf"])
        assert result.exit_code == 0
        assert "wf_new123" in result.output
        mock_client.create_workflow.assert_called_once()


class TestWorkflowRun:

    @patch("appxen_cli.main.get_client")
    def test_run_no_poll(self, mock_get_client):
        """Run with --no-poll returns immediately."""
        mock_client = MagicMock()
        mock_client.run_workflow.return_value = {
            "execution_id": "exec_abc",
            "status": "running",
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "run", "wf_1", "--no-poll"])
        assert result.exit_code == 0
        assert "exec_abc" in result.output

    @patch("appxen_cli.main.get_client")
    def test_run_with_inputs(self, mock_get_client):
        """Run parses key=value inputs."""
        mock_client = MagicMock()
        mock_client.run_workflow.return_value = {
            "execution_id": "exec_abc",
            "status": "running",
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(
            app, ["workflow", "run", "wf_1", "--no-poll", "-i", "segment=enterprise", "-i", "limit=10"]
        )
        assert result.exit_code == 0

        call_kwargs = mock_client.run_workflow.call_args
        inputs = call_kwargs.args[1] if len(call_kwargs.args) > 1 else call_kwargs.kwargs.get("inputs", {})
        # Verify inputs were parsed (checking positional args)
        assert mock_client.run_workflow.called

    @patch("appxen_cli.main.get_client")
    def test_run_invalid_input_format(self, mock_get_client):
        """Run rejects inputs without = separator."""
        mock_client = MagicMock()
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "run", "wf_1", "--no-poll", "-i", "bad_input"])
        assert result.exit_code == 1
        assert "Invalid input format" in result.output

    @patch("appxen_cli.main.get_client")
    def test_run_api_error(self, mock_get_client):
        """Run handles API error."""
        mock_client = MagicMock()
        mock_client.run_workflow.side_effect = Exception("Connection refused")
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "run", "wf_1", "--no-poll"])
        assert result.exit_code == 1
        assert "Failed to start execution" in result.output


class TestWorkflowStatus:

    @patch("appxen_cli.main.get_client")
    def test_status(self, mock_get_client):
        """Status displays execution details."""
        mock_client = MagicMock()
        mock_client.get_execution.return_value = {
            "execution_id": "exec_1",
            "workflow_id": "wf_1",
            "status": "completed",
            "started_at": "2026-02-26T12:00:00Z",
            "completed_at": "2026-02-26T12:01:00Z",
        }
        mock_client.get_steps.return_value = {
            "steps": [
                {
                    "step_id": "s1",
                    "agent_id": "analyst",
                    "status": "completed",
                    "usage": {"input_tokens": 500, "output_tokens": 200},
                }
            ]
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "status", "exec_1"])
        assert result.exit_code == 0
        assert "exec_1" in result.output
        assert "completed" in result.output

    @patch("appxen_cli.main.get_client")
    def test_status_json(self, mock_get_client):
        """Status with --json outputs raw JSON."""
        mock_client = MagicMock()
        mock_client.get_execution.return_value = {
            "execution_id": "exec_1",
            "status": "running",
        }
        mock_client.get_steps.return_value = {"steps": []}
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "status", "exec_1", "--json"])
        assert result.exit_code == 0
        assert '"execution"' in result.output


class TestWorkflowStop:

    @patch("appxen_cli.main.get_client")
    def test_stop_confirmed(self, mock_get_client):
        """Stop with --yes skips confirmation."""
        mock_client = MagicMock()
        mock_client.stop_execution.return_value = {
            "execution_id": "exec_1",
            "status": "aborted",
        }
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "stop", "exec_1", "--yes"])
        assert result.exit_code == 0
        assert "stopped" in result.output
        mock_client.stop_execution.assert_called_once_with("exec_1")


class TestWorkflowAuth:

    def test_not_logged_in(self, monkeypatch, tmp_path):
        """Commands fail when not logged in."""
        monkeypatch.delenv("APPXEN_API_KEY", raising=False)

        import appxen_cli.config as cfg
        monkeypatch.setattr(cfg, "CONFIG_FILE", tmp_path / "nonexistent.toml")

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "list"])
        assert result.exit_code == 1
        assert "Not logged in" in result.output

    @patch("appxen_cli.main.get_client")
    def test_logged_in_works(self, mock_get_client):
        """Commands work when logged in with just API key (no orchestrator URL needed)."""
        mock_client = MagicMock()
        mock_client.list_workflows.return_value = {"workflows": []}
        mock_get_client.return_value = mock_client

        runner = CliRunner()
        result = runner.invoke(app, ["workflow", "list"])
        assert result.exit_code == 0
