"""CLI integration tests via typer.testing.CliRunner."""

from __future__ import annotations

import json
from pathlib import Path
from unittest.mock import MagicMock, patch

import httpx
import pytest
import typer
from typer.testing import CliRunner

from acl_cli import config
from acl_cli.main import app

runner = CliRunner()


@pytest.fixture(autouse=True)
def tmp_config(tmp_path, monkeypatch):
    """Redirect ~/.acl to a temp directory for every test."""
    cfg_dir = tmp_path / ".acl"
    cfg_file = cfg_dir / "config.json"
    monkeypatch.setattr(config, "CONFIG_DIR", cfg_dir)
    monkeypatch.setattr(config, "CONFIG_FILE", cfg_file)
    return cfg_file


# ── 1. acl --help ────────────────────────────────────────────────────────


class TestHelp:
    def test_help_exit_code(self):
        result = runner.invoke(app, ["--help"])
        assert result.exit_code == 0

    def test_help_contains_commands(self):
        result = runner.invoke(app, ["--help"])
        output = result.output.lower()
        assert "init" in output
        assert "chat" in output
        assert "browser" in output


# ── 2. acl init ──────────────────────────────────────────────────────────


class TestInit:
    def test_first_run_registers(self, tmp_config):
        """acl init → POST /auth/register → config saved."""
        mock_resp = MagicMock()
        mock_resp.status_code = 201
        mock_resp.json.return_value = {"api_key": "acl_test_key_42"}
        mock_resp.raise_for_status = MagicMock()

        with patch("acl_cli.commands.init.httpx.post", return_value=mock_resp):
            result = runner.invoke(app, ["init"], input="user@acl.test\nTestUser\n")

        assert result.exit_code == 0
        assert "user@acl.test" in result.output

        data = config.load_config()
        assert data is not None
        assert data["api_key"] == "acl_test_key_42"
        assert data["email"] == "user@acl.test"

    def test_already_configured(self, tmp_config):
        """acl init when already configured → shows email."""
        config.save_config(
            api_key="acl_existing", base_url=config.DEFAULT_BASE_URL, email="old@acl.test"
        )
        result = runner.invoke(app, ["init"])
        assert result.exit_code == 0
        assert "old@acl.test" in result.output

    def test_already_configured_shows_hint(self, tmp_config):
        """acl init when already configured → suggests --reset."""
        config.save_config(
            api_key="acl_existing", base_url=config.DEFAULT_BASE_URL, email="old@acl.test"
        )
        result = runner.invoke(app, ["init"])
        assert "--reset" in result.output


# ── 3. acl browser open ─────────────────────────────────────────────────


def _make_api_key_config(tmp_config):
    """Helper: write a valid config so client.py doesn't abort."""
    config.save_config(api_key="acl_test", base_url="https://fake.acl.test", email="t@t.com")


class TestBrowserOpen:
    def test_browser_open(self, tmp_config):
        """acl browser open example.com → ✅ in output."""
        _make_api_key_config(tmp_config)

        session_resp = MagicMock(spec=httpx.Response)
        session_resp.status_code = 200
        session_resp.json.return_value = {"session_id": "sess_123"}
        session_resp.raise_for_status = MagicMock()

        navigate_resp = MagicMock(spec=httpx.Response)
        navigate_resp.status_code = 200
        navigate_resp.json.return_value = {"title": "Example Domain"}
        navigate_resp.raise_for_status = MagicMock()

        def fake_post(path, **kwargs):
            if "/sessions" in path:
                return session_resp
            return navigate_resp

        mock_client = MagicMock()
        mock_client.__enter__ = MagicMock(return_value=mock_client)
        mock_client.__exit__ = MagicMock(return_value=False)
        mock_client.post = MagicMock(side_effect=fake_post)

        with patch("acl_cli.client.make_client", return_value=mock_client):
            result = runner.invoke(app, ["browser", "open", "https://example.com"])

        assert result.exit_code == 0
        assert "✅" in result.output or "example.com" in result.output.lower()


# ── 4. acl credentials save + list ──────────────────────────────────────


class TestCredentials:
    def test_save_and_list(self, tmp_config):
        """acl credentials save + list → login masked."""
        _make_api_key_config(tmp_config)

        save_resp = MagicMock(spec=httpx.Response)
        save_resp.status_code = 200
        save_resp.json.return_value = {"ok": True}
        save_resp.raise_for_status = MagicMock()

        list_resp = MagicMock(spec=httpx.Response)
        list_resp.status_code = 200
        list_resp.json.return_value = [
            {"site_domain": "kaspi.kz", "auth_type": "login_password", "login": "mylogin123"}
        ]
        list_resp.raise_for_status = MagicMock()

        mock_client = MagicMock()
        mock_client.__enter__ = MagicMock(return_value=mock_client)
        mock_client.__exit__ = MagicMock(return_value=False)

        call_count = {"n": 0}

        def route_request(method):
            def handler(path, **kwargs):
                call_count["n"] += 1
                if method == "post":
                    return save_resp
                return list_resp
            return handler

        mock_client.post = MagicMock(side_effect=route_request("post"))
        mock_client.get = MagicMock(side_effect=route_request("get"))

        with patch("acl_cli.client.make_client", return_value=mock_client):
            # Save credentials
            save_result = runner.invoke(
                app,
                ["credentials", "save", "kaspi.kz"],
                input="mylogin123\nsecretpass\n",
            )
            assert save_result.exit_code == 0

            # List credentials
            list_result = runner.invoke(app, ["credentials", "list"])
            assert list_result.exit_code == 0
            # Login should be masked: first 2 chars + ***
            assert "my***" in list_result.output
            # Full login should NOT appear
            assert "mylogin123" not in list_result.output


# ── 5. acl session list ─────────────────────────────────────────────────


class TestSessionList:
    def test_session_list_table(self, tmp_config):
        """acl session list → table output."""
        _make_api_key_config(tmp_config)

        list_resp = MagicMock(spec=httpx.Response)
        list_resp.status_code = 200
        list_resp.json.return_value = [
            {
                "site_domain": "kaspi.kz",
                "authenticated": True,
                "expires_at": "2026-04-01T00:00:00",
                "last_used_at": "2026-03-24T12:00:00",
            },
            {
                "site_domain": "google.com",
                "authenticated": False,
                "expires_at": "2026-04-02T00:00:00",
                "last_used_at": "2026-03-24T10:00:00",
            },
        ]
        list_resp.raise_for_status = MagicMock()

        mock_client = MagicMock()
        mock_client.__enter__ = MagicMock(return_value=mock_client)
        mock_client.__exit__ = MagicMock(return_value=False)
        mock_client.get = MagicMock(return_value=list_resp)

        with patch("acl_cli.client.make_client", return_value=mock_client):
            result = runner.invoke(app, ["session", "list"])

        assert result.exit_code == 0
        assert "kaspi.kz" in result.output
        assert "google.com" in result.output
