"""
Integration tests — make real HTTP calls to the NotiLens webhook.

Requires environment variables:
    NOTILENS_TOKEN=your_token
    NOTILENS_SECRET=your_secret

Run with:
    NOTILENS_TOKEN=xxx NOTILENS_SECRET=yyy python3 -m pytest tests/test_integration.py -v
"""

import os
import time
import pytest

TOKEN = os.environ.get("NOTILENS_TOKEN", "")
SECRET = os.environ.get("NOTILENS_SECRET", "")

pytestmark = pytest.mark.skipif(
    not TOKEN or not SECRET,
    reason="NOTILENS_TOKEN and NOTILENS_SECRET env vars required for integration tests"
)


@pytest.fixture(scope="module")
def agent():
    import notilens
    notilens._agents.clear()
    notilens._patch_owner = None
    a = notilens.init(agent="integration-test", token=TOKEN, secret=SECRET)
    yield a
    a.shutdown()


def test_task_lifecycle(agent):
    task_id = agent.task_start(task_id=f"integ_{int(time.time())}")
    time.sleep(0.5)
    agent.task_progress("Step 1 running", task_id=task_id)
    time.sleep(0.5)
    agent.task_complete("Integration test done", task_id=task_id)


def test_task_fail(agent):
    task_id = f"integ_fail_{int(time.time())}"
    agent.task_start(task_id=task_id)
    time.sleep(0.5)
    agent.task_fail("Simulated failure", task_id=task_id)


def test_task_error_non_terminal(agent):
    task_id = f"integ_err_{int(time.time())}"
    agent.task_start(task_id=task_id)
    time.sleep(0.5)
    agent.task_error("Non-fatal error occurred", task_id=task_id)
    time.sleep(0.5)
    agent.task_complete("Recovered and done", task_id=task_id)


def test_task_loop(agent):
    task_id = f"integ_loop_{int(time.time())}"
    agent.task_start(task_id=task_id)
    for i in range(3):
        agent.task_loop(f"Loop iteration {i + 1}", task_id=task_id)
        time.sleep(0.3)
    agent.task_complete("Loop test done", task_id=task_id)


def test_task_retry(agent):
    task_id = f"integ_retry_{int(time.time())}"
    agent.task_start(task_id=task_id)
    time.sleep(0.3)
    agent.task_retry(task_id=task_id)
    time.sleep(0.3)
    agent.task_complete("Succeeded after retry", task_id=task_id)


def test_input_events(agent):
    task_id = f"integ_input_{int(time.time())}"
    agent.task_start(task_id=task_id)
    agent.input_required("Please confirm the output", task_id=task_id)
    time.sleep(0.3)
    agent.input_approved("User confirmed", task_id=task_id)
    time.sleep(0.3)
    agent.task_complete("Input approved, done", task_id=task_id)


def test_input_rejected(agent):
    task_id = f"integ_reject_{int(time.time())}"
    agent.task_start(task_id=task_id)
    agent.input_required("Please confirm", task_id=task_id)
    time.sleep(0.3)
    agent.input_rejected("User rejected", task_id=task_id)
    time.sleep(0.3)
    agent.task_cancel("Cancelled after rejection", task_id=task_id)


def test_ai_response_events(agent):
    task_id = f"integ_ai_{int(time.time())}"
    agent.task_start(task_id=task_id)
    agent.ai_response_generated("Summary: the document is about X", task_id=task_id)
    time.sleep(0.3)
    agent.task_complete("AI response delivered", task_id=task_id)


def test_ai_response_failed(agent):
    task_id = f"integ_aifail_{int(time.time())}"
    agent.task_start(task_id=task_id)
    agent.ai_response_failed("Model timeout", task_id=task_id)
    time.sleep(0.3)
    agent.task_fail("Could not get AI response", task_id=task_id)


def test_custom_emit(agent):
    agent.emit("order.placed", "Order #1234", meta={"amount": 99.99, "currency": "USD"})
    agent.emit("disk.space.full", "Only 1GB remaining", level="warning")
    agent.emit("user.registered", "New user signup", meta={"plan": "pro"})
    time.sleep(0.5)


def test_task_timeout(agent):
    task_id = f"integ_timeout_{int(time.time())}"
    agent.task_start(task_id=task_id)
    time.sleep(0.3)
    agent.task_timeout("Exceeded 30s limit", task_id=task_id)


def test_task_cancel(agent):
    task_id = f"integ_cancel_{int(time.time())}"
    agent.task_start(task_id=task_id)
    time.sleep(0.3)
    agent.task_cancel("User cancelled the job", task_id=task_id)


def test_emitter_direct():
    """Test delivery layer directly — no SDK wrapper."""
    from notilens.emitter import NotiLensEmitter
    emitter = NotiLensEmitter(token=TOKEN, secret=SECRET, agent="emitter-direct-test")
    emitter.send("task.started", "Direct emitter test", level="info", meta={"task_id": "direct-001", "source": "integration"})
    emitter.flush(timeout=5.0)
    emitter.shutdown()


# ── CLI integration tests ─────────────────────────────────────────────────────

class TestCLI:
    """Test CLI commands with real delivery."""

    @pytest.fixture(autouse=True)
    def setup_agent(self, tmp_path, monkeypatch):
        from notilens.config import save_agent
        monkeypatch.setattr("notilens.config.CONFIG_FILE", tmp_path / ".notilens_config.json")
        save_agent("cli-test", TOKEN, SECRET)
        # reset emitter cache between tests
        import notilens.cli as cli_mod
        cli_mod._emitter_cache.clear()
        yield
        cli_mod._emitter_cache.clear()

    def _run(self, *args):
        import sys
        from unittest.mock import patch
        import notilens.cli as cli_mod
        with patch.object(sys, "argv", ["notilens", *args]):
            cli_mod.main()

    def test_cli_task_lifecycle(self):
        task_id = f"cli_{int(time.time())}"
        self._run("task.start", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.5)
        self._run("task.progress", "Step 1", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.5)
        self._run("task.complete", "CLI task done", "--agent", "cli-test", "--task", task_id)

    def test_cli_task_fail(self):
        task_id = f"cli_fail_{int(time.time())}"
        self._run("task.start", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.5)
        self._run("task.fail", "CLI task failed", "--agent", "cli-test", "--task", task_id)

    def test_cli_task_error(self):
        task_id = f"cli_err_{int(time.time())}"
        self._run("task.start", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.3)
        self._run("task.error", "Non-fatal CLI error", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.3)
        self._run("task.complete", "Recovered", "--agent", "cli-test", "--task", task_id)

    def test_cli_task_loop(self):
        task_id = f"cli_loop_{int(time.time())}"
        self._run("task.start", "--agent", "cli-test", "--task", task_id)
        for i in range(3):
            self._run("task.loop", f"Loop {i+1}", "--agent", "cli-test", "--task", task_id)
            time.sleep(0.3)
        self._run("task.complete", "Loop done", "--agent", "cli-test", "--task", task_id)

    def test_cli_input_events(self):
        task_id = f"cli_input_{int(time.time())}"
        self._run("task.start", "--agent", "cli-test", "--task", task_id)
        self._run("input.required", "Please confirm", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.3)
        self._run("input.approve", "Approved", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.3)
        self._run("task.complete", "Input done", "--agent", "cli-test", "--task", task_id)

    def test_cli_custom_emit(self):
        self._run("emit", "order.placed", "Order #999", "--agent", "cli-test", "--meta", "amount=49.99")
        time.sleep(0.3)
        self._run("emit", "disk.space.full", "Only 1GB left", "--agent", "cli-test", "--level", "warning")

    def test_cli_ai_response(self):
        task_id = f"cli_ai_{int(time.time())}"
        self._run("task.start", "--agent", "cli-test", "--task", task_id)
        self._run("ai.response.generate", "Summary generated", "--agent", "cli-test", "--task", task_id)
        time.sleep(0.3)
        self._run("task.complete", "AI response done", "--agent", "cli-test", "--task", task_id)


# ── Auto-patch integration tests ──────────────────────────────────────────────

class TestAutoPatch:
    """Test auto-patching of AI frameworks with real delivery."""

    @pytest.fixture(autouse=True)
    def setup(self):
        import notilens
        notilens._agents.clear()
        notilens._patch_owner = None
        yield
        for a in notilens._agents.values():
            a.shutdown()
        notilens._agents.clear()
        notilens._patch_owner = None

    def test_openai_patch(self):
        """Real OpenAI call — ai.call.start + ai.call.complete fire automatically."""
        pytest.importorskip("openai")
        import openai
        import notilens

        openai_key = os.environ.get("OPENAI_API_KEY", "")
        if not openai_key:
            pytest.skip("OPENAI_API_KEY not set")

        agent = notilens.init(
            agent="patch-openai-test",
            token=TOKEN,
            secret=SECRET,
            patch=True,
        )
        task_id = agent.task_start(task_id=f"patch_oai_{int(time.time())}")

        client = openai.OpenAI(api_key=openai_key)
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": "Say hello in one word."}],
            max_tokens=5,
        )
        agent.ai_response_generated(response.choices[0].message.content, task_id=task_id)
        agent.task_complete("OpenAI patch test done", task_id=task_id)

    def test_anthropic_patch(self):
        """Real Anthropic call — ai.call.start + ai.call.complete fire automatically."""
        pytest.importorskip("anthropic")
        import anthropic
        import notilens

        anthropic_key = os.environ.get("ANTHROPIC_API_KEY", "")
        if not anthropic_key:
            pytest.skip("ANTHROPIC_API_KEY not set")

        agent = notilens.init(
            agent="patch-anthropic-test",
            token=TOKEN,
            secret=SECRET,
            patch=True,
        )
        task_id = agent.task_start(task_id=f"patch_ant_{int(time.time())}")

        client = anthropic.Anthropic(api_key=anthropic_key)
        response = client.messages.create(
            model="claude-haiku-4-5-20251001",
            max_tokens=5,
            messages=[{"role": "user", "content": "Say hello in one word."}],
        )
        agent.ai_response_generated(response.content[0].text, task_id=task_id)
        agent.task_complete("Anthropic patch test done", task_id=task_id)

    def test_patch_without_real_call(self):
        """Verify patch installs without API key — just checks patching works."""
        import notilens
        agent = notilens.init(
            agent="patch-nokey-test",
            token=TOKEN,
            secret=SECRET,
            patch=True,
        )
        # manually fire what the patcher would fire
        task_id = agent.task_start(task_id=f"patch_nokey_{int(time.time())}")
        agent._emitter.send("ai.call.start", "Simulated AI call", level="info",
                            meta={"model": "test-model", "task_id": task_id})
        agent._emitter.send("ai.call.complete", "Simulated AI done", level="info",
                            meta={"model": "test-model", "duration": 0.5, "task_id": task_id})
        agent.task_complete("Patch simulation done", task_id=task_id)
