"""Tests for influxion.init() and client fallback from global state."""

from __future__ import annotations

import json

import httpx
import pytest
import respx

from influxion import AsyncInfluxionClient, InfluxionClient, init

from .conftest import BASE_URL, TEST_API_KEY, TEST_PROJECT_ID


# ---------------------------------------------------------------------------
# init() behaviour
# ---------------------------------------------------------------------------


def test_init_stores_all_values(reset_global):
    """init() stores api_key, base_url, project_id, and agent_id in global state."""
    from influxion._base import _global

    init(
        api_key="sk-from-init",
        base_url="https://init.example/v1",
        project_id="proj-123",
        agent_id="agent-456",
    )
    assert _global["api_key"] == "sk-from-init"
    assert _global["base_url"] == "https://init.example/v1"
    assert _global["project_id"] == "proj-123"
    assert _global["agent_id"] == "agent-456"


def test_init_partial_update_leaves_others_unchanged(reset_global):
    """Calling init() with only some kwargs updates those and leaves the rest unchanged."""
    from influxion._base import _global

    init(api_key="sk-first", base_url="https://first.example/v1")
    assert _global["api_key"] == "sk-first"
    assert _global["base_url"] == "https://first.example/v1"
    assert _global["project_id"] is None
    assert _global["agent_id"] is None

    init(project_id="proj-only")
    assert _global["api_key"] == "sk-first"
    assert _global["base_url"] == "https://first.example/v1"
    assert _global["project_id"] == "proj-only"
    assert _global["agent_id"] is None


def test_init_omitted_keys_unchanged(reset_global):
    """Passing None explicitly is not stored; only provided keys are updated."""
    from influxion._base import _global

    init(api_key="sk-set")
    assert _global["api_key"] == "sk-set"

    # init() only updates when value is not None; we don't have a way to "clear"
    # via init(), so base_url stays None
    assert _global["base_url"] is None


def test_init_raises_when_no_api_key_and_no_env(monkeypatch, reset_global):
    """init() raises AuthenticationError when no api_key is provided and env is unset."""
    from influxion._exceptions import AuthenticationError

    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)

    with pytest.raises(AuthenticationError, match="No API key"):
        init(project_id="proj-only")


def test_init_succeeds_when_api_key_from_env(monkeypatch, reset_global):
    """init() succeeds when api_key is not passed but INFLUXION_API_KEY is set."""
    monkeypatch.setenv("INFLUXION_API_KEY", TEST_API_KEY)

    init(project_id="proj-only")  # no api_key arg; env provides it


# ---------------------------------------------------------------------------
# Client fallback: api_key and base_url from global
# ---------------------------------------------------------------------------


@respx.mock
def test_sync_client_uses_global_api_key_and_base_url(monkeypatch, reset_global):
    """InfluxionClient() uses api_key and base_url from init() when not passed."""
    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)
    monkeypatch.delenv("INFLUXION_BASE_URL", raising=False)

    init(api_key=TEST_API_KEY, base_url=BASE_URL)

    route = respx.post(f"{BASE_URL}/agent_sessions").mock(
        return_value=httpx.Response(201, json={})
    )
    c = InfluxionClient(fail_silently=False)
    result = c.create_session(
        session_id="x",
        name="x",
        project_id=TEST_PROJECT_ID,
        agent_id="a",
    )
    c.close()

    assert result is None
    assert route.called
    assert route.calls.last.request.headers["authorization"] == f"Bearer {TEST_API_KEY}"


@respx.mock
async def test_async_client_uses_global_api_key_and_base_url(
    monkeypatch, reset_global
):
    """AsyncInfluxionClient() uses api_key and base_url from init() when not passed."""
    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)
    monkeypatch.delenv("INFLUXION_BASE_URL", raising=False)

    init(api_key=TEST_API_KEY, base_url=BASE_URL)

    route = respx.post(f"{BASE_URL}/agent_sessions").mock(
        return_value=httpx.Response(201, json={})
    )
    c = AsyncInfluxionClient(fail_silently=False)
    result = await c.create_session(
        session_id="x",
        name="x",
        project_id=TEST_PROJECT_ID,
        agent_id="a",
    )
    await c.aclose()

    assert result is None
    assert route.called
    assert route.calls.last.request.headers["authorization"] == f"Bearer {TEST_API_KEY}"


@respx.mock
def test_explicit_client_args_override_global(monkeypatch, reset_global):
    """Explicit api_key and base_url on the client override global state."""
    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)
    monkeypatch.delenv("INFLUXION_BASE_URL", raising=False)

    init(api_key="sk-global-overridden", base_url="https://global.example/v1")

    # Client passes explicit values; request should go to BASE_URL with TEST_API_KEY
    route = respx.post(f"{BASE_URL}/agent_sessions").mock(
        return_value=httpx.Response(201, json={})
    )
    c = InfluxionClient(
        api_key=TEST_API_KEY,
        base_url=BASE_URL,
        fail_silently=False,
    )
    result = c.create_session(
        session_id="x",
        name="x",
        project_id=TEST_PROJECT_ID,
        agent_id="a",
    )
    c.close()

    assert result is None
    assert route.called
    assert route.calls.last.request.headers["authorization"] == f"Bearer {TEST_API_KEY}"


def test_missing_api_key_raises_when_no_global_and_no_env(monkeypatch, reset_global):
    """When init() and env do not provide api_key, client construction raises."""
    from influxion._exceptions import AuthenticationError

    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)
    monkeypatch.delenv("INFLUXION_BASE_URL", raising=False)

    with pytest.raises(AuthenticationError, match="No API key"):
        InfluxionClient()


async def test_async_missing_api_key_raises_when_no_global_and_no_env(
    monkeypatch, reset_global
):
    """When init() and env do not provide api_key, async client construction raises."""
    from influxion._exceptions import AuthenticationError

    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)
    monkeypatch.delenv("INFLUXION_BASE_URL", raising=False)

    with pytest.raises(AuthenticationError, match="No API key"):
        AsyncInfluxionClient()


# ---------------------------------------------------------------------------
# create_session fallback: project_id and agent_id from global
# ---------------------------------------------------------------------------


@respx.mock
def test_create_session_uses_global_project_id_and_agent_id(
    monkeypatch, reset_global
):
    """create_session() uses project_id and agent_id from init() when not passed."""
    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)
    monkeypatch.delenv("INFLUXION_BASE_URL", raising=False)

    init(
        api_key=TEST_API_KEY,
        base_url=BASE_URL,
        project_id=TEST_PROJECT_ID,
        agent_id="report-agent",
    )
    c = InfluxionClient(fail_silently=False)

    route = respx.post(f"{BASE_URL}/agent_sessions").mock(
        return_value=httpx.Response(201, json={})
    )
    result = c.create_session(
        session_id="weekly-run", name="weekly-run"
    )
    c.close()

    assert result is None
    assert route.called
    body = json.loads(route.calls.last.request.content)
    assert body["project_id"] == TEST_PROJECT_ID
    assert body["agent_id"] == "report-agent"


@respx.mock
async def test_async_create_session_uses_global_project_id_and_agent_id(
    monkeypatch, reset_global
):
    """create_session() uses project_id and agent_id from init() when not passed."""
    monkeypatch.delenv("INFLUXION_API_KEY", raising=False)
    monkeypatch.delenv("INFLUXION_BASE_URL", raising=False)

    init(
        api_key=TEST_API_KEY,
        base_url=BASE_URL,
        project_id=TEST_PROJECT_ID,
        agent_id="report-agent",
    )
    c = AsyncInfluxionClient(fail_silently=False)

    route = respx.post(f"{BASE_URL}/agent_sessions").mock(
        return_value=httpx.Response(201, json={})
    )
    result = await c.create_session(
        session_id="weekly-run", name="weekly-run"
    )
    await c.aclose()

    assert result is None
    assert route.called
    body = json.loads(route.calls.last.request.content)
    assert body["project_id"] == TEST_PROJECT_ID
    assert body["agent_id"] == "report-agent"


def test_create_session_raises_when_project_id_missing(monkeypatch, reset_global):
    """create_session() raises when project_id is not passed and not in global."""
    from influxion import InfluxionClient
    from influxion._exceptions import InfluxionError

    monkeypatch.setenv("INFLUXION_API_KEY", TEST_API_KEY)
    init(api_key=TEST_API_KEY, agent_id="my-agent")  # no project_id

    c = InfluxionClient(fail_silently=False)
    with pytest.raises(InfluxionError, match="project_id is required"):
        c.create_session(
            session_id="x", name="x", agent_id="my-agent"
        )
    c.close()


def test_create_session_raises_when_agent_id_missing(monkeypatch, reset_global):
    """create_session() raises when agent_id is not passed and not in global."""
    from influxion import InfluxionClient
    from influxion._exceptions import InfluxionError

    monkeypatch.setenv("INFLUXION_API_KEY", TEST_API_KEY)
    init(api_key=TEST_API_KEY, project_id=TEST_PROJECT_ID)  # no agent_id

    c = InfluxionClient(fail_silently=False)
    with pytest.raises(InfluxionError, match="agent_id is required"):
        c.create_session(
            session_id="x", name="x", project_id=TEST_PROJECT_ID
        )
    c.close()
