"""Add/remove workspace for the managed Slack relay (M3.5 Step 2).

Add = the managed OAuth callback writes `slack:team:<id>` and hot-reloads the
gateway (no app restart). Remove = per-workspace disconnect: cloud routing row
deleted best-effort, local token dropped, gateway reloaded; the LAST removal
flips the connector off without resurrecting any stored manual creds.
"""

from __future__ import annotations

import pytest
from fastapi.testclient import TestClient

from coworker import cloud
from coworker.server import SessionManager, create_app


@pytest.fixture
def client(tmp_path, monkeypatch):
    monkeypatch.setenv("COWORKER_STATE_DIR", str(tmp_path / "state"))
    manager = SessionManager(workspace=tmp_path)
    app = create_app(manager)
    with TestClient(app) as c:
        c.manager = manager
        yield c


def _install_form(team_id: str) -> dict:
    state = f"slack-{team_id}"
    cloud._pending_managed_states[state] = cloud._now()
    return {
        "connector": "slack",
        "team_id": team_id,
        "access_token": f"xoxb-{team_id}",
        "bot_user_id": "B1",
        "slack_user_id": f"U_{team_id}",
        "account": f"Workspace {team_id}",
        "team_domain": f"dom-{team_id.lower()}",
        "connection_id": f"conn_{team_id}",
        "app_state": state,
    }


def _no_cloud(monkeypatch):
    """The cloud row delete is best-effort HTTP; record instead of calling out."""
    import coworker.cloud as cloud

    calls: list[str] = []
    monkeypatch.setattr(
        cloud, "slack_disconnect_workspace", lambda s, c, team_id: calls.append(team_id)
    )
    return calls


def test_managed_callback_installs_and_hot_reloads(client, monkeypatch):
    refreshes = []

    async def _refresh():
        refreshes.append(True)
        return []

    monkeypatch.setattr(client.manager, "refresh_gateway", _refresh)
    resp = client.post("/oauth/callback", data=_install_form("T1"))
    assert resp.status_code == 200 and "Slack connected" in resp.text
    assert client.manager.secrets.get("slack:team:T1")["bot_token"] == "xoxb-T1"
    # The broker-resolved workspace domain persists (names collide; domains don't)
    # and rides the workspaces list for the GUI's group headers.
    assert client.manager.secrets.get("slack:team:T1")["domain"] == "dom-t1"
    slack = [
        c
        for c in client.get("/v1/connectors").json()["connectors"]
        if c["name"] == "slack"
    ][0]
    assert [w["domain"] for w in slack["workspaces"]] == ["dom-t1"]
    assert slack["workspaces"][0]["approval_owner_ids"] == ["U_T1"]
    assert client.manager.secrets.get("slack:default")["mode"] == "relay"
    assert refreshes  # new workspace's token loads without an app restart

    # a second workspace instals alongside, not instead
    client.post("/oauth/callback", data=_install_form("T2"))
    assert client.manager.secrets.get("slack:team:T1") is not None
    assert client.manager.secrets.get("slack:team:T2") is not None


def test_disconnect_one_workspace_keeps_the_other(client, monkeypatch):
    cloud_calls = _no_cloud(monkeypatch)
    for t in ("T1", "T2"):
        client.post("/oauth/callback", data=_install_form(t))

    body = client.post("/v1/connectors/slack/workspaces/T1/disconnect").json()
    assert body["ok"] is True and body["remaining_workspaces"] == 1
    assert cloud_calls == ["T1"]
    assert client.manager.secrets.get("slack:team:T1") is None
    assert client.manager.secrets.get("slack:team:T2") is not None
    # connector still connected in relay mode for the surviving workspace
    slack = next(c for c in client.manager.list_connectors() if c["name"] == "slack")
    assert slack["connected"] is True
    assert [w["team_id"] for w in slack["workspaces"]] == ["T2"]


def test_disconnect_last_workspace_flips_connector_off(client, monkeypatch):
    _no_cloud(monkeypatch)
    client.post("/oauth/callback", data=_install_form("T1"))

    body = client.post("/v1/connectors/slack/workspaces/T1/disconnect").json()
    assert body["ok"] is True and body["remaining_workspaces"] == 0
    assert client.manager.secrets.get("slack:default") is None
    slack = next(c for c in client.manager.list_connectors() if c["name"] == "slack")
    assert slack["connected"] is False


def test_last_disconnect_never_resurrects_manual_creds(client, monkeypatch):
    # Manual Socket Mode tokens stored BEFORE the relay switch must stay stored
    # but disabled — removing the last workspace must not start listening on them.
    _no_cloud(monkeypatch)
    client.manager.secrets.put(
        "slack:default",
        {"type": "token", "bot_token": "xoxb-manual", "app_token": "xapp-manual"},
    )
    client.post("/oauth/callback", data=_install_form("T1"))
    client.post("/v1/connectors/slack/workspaces/T1/disconnect")

    default = client.manager.secrets.get("slack:default")
    assert default["bot_token"] == "xoxb-manual"  # creds kept for a manual re-enable
    assert default["enabled"] is False and "mode" not in default
    from coworker.connectors import load_settings

    assert load_settings(client.manager.secrets)["slack"].enabled is False


def test_disconnect_unknown_workspace_errors(client, monkeypatch):
    _no_cloud(monkeypatch)
    body = client.post("/v1/connectors/slack/workspaces/T_NOPE/disconnect").json()
    assert body["ok"] is False
