"""
Tests for the CLI entry point via Click's test runner.
"""

from __future__ import annotations

import json
import textwrap
from pathlib import Path

import pytest
from click.testing import CliRunner

from hallucination_grep.cli import main


class TestCLI:
    def _runner(self):
        return CliRunner()

    # ------------------------------------------------------------------
    # Basic smoke tests
    # ------------------------------------------------------------------

    def test_reads_from_file(self, tmp_codebase: Path, tmp_path: Path) -> None:
        input_file = tmp_path / "response.txt"
        input_file.write_text(
            "Use get_user_data() to fetch data.\n", encoding="utf-8"
        )
        runner = self._runner()
        result = runner.invoke(
            main,
            [str(input_file), "--codebase", str(tmp_codebase), "--no-color"],
            catch_exceptions=False,
        )
        # The command should run without crashing (2 = usage error, not expected)
        assert result.exit_code != 2

    def test_reads_from_stdin(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--no-color"],
            input="Call get_user_data() for results.\n",
            catch_exceptions=False,
        )
        assert result.exit_code in (0, 1)  # 0=clean, 1=hallucinations found

    def test_json_output_structure(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--json"],
            input="Call get_user_profile() which is fake.\n",
            catch_exceptions=False,
        )
        data = json.loads(result.output)
        assert "summary" in data
        assert "hallucinations" in data
        assert "verified" in data
        assert "total_references" in data["summary"]
        assert "hallucination_rate" in data["summary"]

    def test_json_exit_code_1_on_hallucination(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--json"],
            input="Call totally_nonexistent_function_xyz().\n",
            catch_exceptions=False,
        )
        data = json.loads(result.output)
        assert data["summary"]["hallucination_count"] >= 1
        assert result.exit_code == 1

    def test_exit_code_0_when_all_verified(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--json"],
            input="Call get_user_data() which exists.\n",
            catch_exceptions=False,
        )
        data = json.loads(result.output)
        # get_user_data should be verified
        verified_names = [r["name"] for r in data["verified"]]
        assert "get_user_data" in verified_names
        # Exit code depends on whether any other refs were hallucinated
        assert result.exit_code in (0, 1)

    def test_check_filter_cli(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--json", "--check", "functions"],
            input="from os import path\nCall my_fake_function().\n",
            catch_exceptions=False,
        )
        data = json.loads(result.output)
        # imports should not appear in results when --check functions
        all_kinds = (
            [r["kind"] for r in data["hallucinations"]] +
            [r["kind"] for r in data["verified"]]
        )
        assert "import" not in all_kinds

    def test_invalid_check_type(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--check", "bananas"],
            input="some input\n",
        )
        assert result.exit_code == 2

    def test_version_flag(self) -> None:
        runner = self._runner()
        result = runner.invoke(main, ["--version"])
        assert "0.1.0" in result.output
        assert result.exit_code == 0

    def test_help_flag(self) -> None:
        runner = self._runner()
        result = runner.invoke(main, ["--help"])
        assert "codebase" in result.output
        assert result.exit_code == 0

    def test_json_hallucination_has_suggestion(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--json"],
            input="Call get_user_profile() which is similar to get_user_data.\n",
            catch_exceptions=False,
        )
        data = json.loads(result.output)
        hallucinations = data["hallucinations"]
        # get_user_profile should be flagged and suggest get_user_data
        profile_halluc = [h for h in hallucinations if h["name"] == "get_user_profile"]
        if profile_halluc:
            # difflib picks the closest match — get_user_by_id or get_user_data are both valid
            assert profile_halluc[0]["suggestion_name"] in ("get_user_by_id", "get_user_data")

    def test_rich_output_contains_report_header(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase), "--no-color"],
            input="Call fake_function_xyz().\n",
            catch_exceptions=False,
        )
        assert "HALLUCINATION GREP REPORT" in result.output

    def test_empty_input_exits_with_error(self, tmp_codebase: Path) -> None:
        runner = self._runner()
        result = runner.invoke(
            main,
            ["--codebase", str(tmp_codebase)],
            input="",
        )
        assert result.exit_code == 2
