"""Channel management and webhook API endpoints."""

from __future__ import annotations

import logging

from fastapi import APIRouter, Request

from agntspace.infra.i18n import t

log = logging.getLogger(__name__)

router = APIRouter(prefix="/channels", tags=["channels"])


def _persist_channel(request: Request, channel_name: str, config: dict) -> None:
    """Save channel credentials to the encrypted Fernet store.

    BINDING RULE: bot tokens, access tokens, API keys, and any other
    long-lived secret a channel needs at runtime MUST land in the encrypted
    credential store via this helper — never in config.toml. The helper also
    strips any legacy plaintext copy from config.toml as a safety net.
    """
    from agntspace.infra.credentials import save_channel_credentials

    save_channel_credentials(
        channel_name,
        config,
        credential_store=getattr(request.app.state, "credential_store", None),
        settings_service=getattr(request.app.state, "settings_service", None),
    )


def _clear_channel(request: Request, channel_name: str) -> None:
    """Remove channel credentials from BOTH stores (encrypted + legacy)."""
    from agntspace.infra.credentials import clear_channel_credentials

    clear_channel_credentials(
        channel_name,
        credential_store=getattr(request.app.state, "credential_store", None),
        settings_service=getattr(request.app.state, "settings_service", None),
    )


@router.get("")
async def list_channels(request: Request) -> list[dict]:
    """List all registered channels with connection status."""
    manager = request.app.state.channel_manager
    return manager.list_channels()


@router.post("/imessage/connect")
async def imessage_connect(request: Request) -> dict:
    """Connect to iMessage (macOS only)."""
    manager = request.app.state.channel_manager
    existing = manager.get_channel("imessage")
    if existing and existing.is_connected:
        return {"ok": True, "already": True}

    from agntspace.channels.imessage import IMessageChannel
    imsg = IMessageChannel()
    try:
        manager.register(imsg)
        await imsg.connect()
        return {"ok": True}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/imessage/disconnect")
async def imessage_disconnect(request: Request) -> dict:
    channel = request.app.state.channel_manager.get_channel("imessage")
    if not channel:
        return {"ok": False, "error": t("api.channels.imessage_not_connected")}
    await channel.disconnect()
    return {"ok": True}


@router.post("/teams/webhook")
async def teams_webhook(request: Request) -> dict:
    """Receive Bot Framework activity from Azure."""
    channel = request.app.state.channel_manager.get_channel("teams")
    if not channel:
        return {"ok": False, "error": t("api.channels.teams_not_configured")}
    payload = await request.json()
    response = await channel.handle_webhook(payload)
    return {"ok": True, "response": response is not None}


@router.post("/teams/connect")
async def teams_connect(request: Request) -> dict:
    """Connect to Microsoft Teams via Graph API."""
    body = await request.json()
    client_id = body.get("client_id", "")
    client_secret = body.get("client_secret", "")
    tenant_id = body.get("tenant_id", "")
    if not all([client_id, client_secret, tenant_id]):
        return {"ok": False, "error": t("api.channels.teams_config_missing")}

    manager = request.app.state.channel_manager
    existing = manager.get_channel("teams")
    if existing and existing.is_connected:
        await existing.disconnect()

    from agntspace.channels.teams import TeamsChannel
    teams = TeamsChannel(client_id, client_secret, tenant_id, bot_id=body.get("bot_id", ""))
    try:
        manager.register(teams)
        await teams.connect()
        return {"ok": True}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/teams/disconnect")
async def teams_disconnect(request: Request) -> dict:
    channel = request.app.state.channel_manager.get_channel("teams")
    if not channel:
        return {"ok": False, "error": t("api.channels.teams_not_connected")}
    await channel.disconnect()
    return {"ok": True}


@router.post("/telegram/connect")
async def telegram_connect(request: Request) -> dict:
    """Connect to Telegram via Bot API token."""
    body = await request.json()
    bot_token = body.get("bot_token", "").strip()
    webhook_url = body.get("webhook_url", "").strip()
    if not bot_token:
        return {"ok": False, "error": t("api.channels.telegram_token_missing", fallback="Bot token is required")}

    manager = request.app.state.channel_manager
    existing = manager.get_channel("telegram")
    if existing and existing.is_connected:
        await existing.disconnect()

    from agntspace.channels.telegram import TelegramChannel

    tg = TelegramChannel(bot_token, webhook_url)
    try:
        await tg.connect()
        manager.register(tg)
        _persist_channel(request, "telegram", {"bot_token": bot_token, "webhook_url": webhook_url})
        return {"ok": True, **tg.get_info()}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/telegram/disconnect")
async def telegram_disconnect(request: Request) -> dict:
    """Disconnect Telegram bot."""
    channel = request.app.state.channel_manager.get_channel("telegram")
    if not channel:
        return {"ok": False, "error": t("api.channels.telegram_not_connected", fallback="Telegram not connected")}
    await channel.disconnect()
    _clear_channel(request, "telegram")
    return {"ok": True}


@router.get("/telegram/status")
async def telegram_status(request: Request) -> dict:
    """Get Telegram connection status."""
    channel = request.app.state.channel_manager.get_channel("telegram")
    if not channel:
        return {"connected": False}
    return {"connected": channel.is_connected, **channel.get_info()}


@router.post("/telegram/webhook")
async def telegram_webhook(request: Request) -> dict:
    """Receive Telegram webhook updates."""
    channel = request.app.state.channel_manager.get_channel("telegram")
    if not channel:
        return {"ok": False, "error": t("api.channels.telegram_not_configured")}

    payload = await request.json()
    response = await channel.handle_webhook(payload)
    return {"ok": True, "response": response is not None}


@router.post("/discord/connect")
async def discord_connect(request: Request) -> dict:
    """Connect to Discord via Bot token."""
    body = await request.json()
    bot_token = body.get("bot_token", "").strip()
    if not bot_token:
        return {"ok": False, "error": t("api.channels.discord_token_missing", fallback="Bot token is required")}

    manager = request.app.state.channel_manager
    existing = manager.get_channel("discord")
    if existing and existing.is_connected:
        await existing.disconnect()

    from agntspace.channels.discord import DiscordChannel

    dc = DiscordChannel(bot_token)
    try:
        await dc.connect()
        # Only register after successful connect
        manager.register(dc)
        _persist_channel(request, "discord", {"bot_token": bot_token})
        return {"ok": True, **dc.get_info()}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/discord/disconnect")
async def discord_disconnect(request: Request) -> dict:
    """Disconnect Discord bot."""
    channel = request.app.state.channel_manager.get_channel("discord")
    if not channel:
        return {"ok": False, "error": t("api.channels.discord_not_connected", fallback="Discord not connected")}
    await channel.disconnect()
    _clear_channel(request, "discord")
    return {"ok": True}


@router.post("/discord/webhook")
async def discord_webhook(request: Request) -> dict:
    """Receive Discord webhook events."""
    channel = request.app.state.channel_manager.get_channel("discord")
    if not channel:
        return {"ok": False, "error": t("api.channels.discord_not_configured")}
    payload = await request.json()
    response = await channel.handle_webhook(payload)
    return {"ok": True, "response": response is not None}


@router.post("/slack/connect")
async def slack_connect(request: Request) -> dict:
    """Connect to Slack via Bot token."""
    body = await request.json()
    bot_token = body.get("bot_token", "").strip()
    if not bot_token:
        return {"ok": False, "error": t("api.channels.slack_token_missing", fallback="Bot token is required")}

    manager = request.app.state.channel_manager
    existing = manager.get_channel("slack")
    if existing and existing.is_connected:
        await existing.disconnect()

    from agntspace.channels.slack import SlackChannel

    sc = SlackChannel(bot_token)
    try:
        await sc.connect()
        # Only register after successful connect
        manager.register(sc)
        _persist_channel(request, "slack", {"bot_token": bot_token})
        return {"ok": True, **sc.get_info()}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/slack/disconnect")
async def slack_disconnect(request: Request) -> dict:
    """Disconnect Slack bot."""
    channel = request.app.state.channel_manager.get_channel("slack")
    if not channel:
        return {"ok": False, "error": t("api.channels.slack_not_connected", fallback="Slack not connected")}
    await channel.disconnect()
    _clear_channel(request, "slack")
    return {"ok": True}


@router.post("/slack/webhook")
async def slack_webhook(request: Request) -> dict:
    """Receive Slack Events API webhooks."""
    channel = request.app.state.channel_manager.get_channel("slack")
    if not channel:
        return {"ok": False, "error": t("api.channels.slack_not_configured")}
    payload = await request.json()
    # Handle Slack URL verification
    if payload.get("type") == "url_verification":
        return {"challenge": payload.get("challenge", "")}
    response = await channel.handle_event(payload)
    return {"ok": True, "response": response is not None}


@router.post("/signal/webhook")
async def signal_webhook(request: Request) -> dict:
    """Receive Signal webhook events from signal-cli REST API."""
    channel = request.app.state.channel_manager.get_channel("signal")
    if not channel:
        return {"ok": False, "error": t("api.channels.signal_not_configured")}
    payload = await request.json()
    response = await channel.handle_webhook(payload)
    return {"ok": True, "response": response is not None}


@router.post("/signal/connect")
async def signal_connect(request: Request) -> dict:
    """Connect to Signal via signal-cli REST API."""
    body = await request.json()
    api_url = body.get("api_url", "http://localhost:8080")
    number = body.get("number", "")
    if not number:
        return {"ok": False, "error": t("api.channels.signal_number_missing")}

    manager = request.app.state.channel_manager
    existing = manager.get_channel("signal")
    if existing and existing.is_connected:
        await existing.disconnect()

    from agntspace.channels.signal import SignalChannel
    signal = SignalChannel(api_url, number)
    try:
        manager.register(signal)
        await signal.connect()
        return {"ok": True, "number": number}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/signal/disconnect")
async def signal_disconnect(request: Request) -> dict:
    """Disconnect from Signal."""
    channel = request.app.state.channel_manager.get_channel("signal")
    if not channel:
        return {"ok": False, "error": t("api.channels.signal_not_connected")}
    await channel.disconnect()
    return {"ok": True}


@router.post("/matrix/webhook")
async def matrix_webhook(request: Request) -> dict:
    """Receive Matrix appservice events (optional — sync loop handles most cases)."""
    channel = request.app.state.channel_manager.get_channel("matrix")
    if not channel:
        return {"ok": False, "error": t("api.channels.matrix_not_configured")}
    payload = await request.json()
    response = await channel.handle_webhook(payload)
    return {"ok": True, "response": response is not None}


@router.post("/matrix/connect")
async def matrix_connect(request: Request) -> dict:
    """Connect to Matrix homeserver."""
    body = await request.json()
    homeserver = body.get("homeserver", "")
    user_id = body.get("user_id", "")
    access_token = body.get("access_token", "")
    if not all([homeserver, user_id, access_token]):
        return {"ok": False, "error": t("api.channels.matrix_config_missing")}

    manager = request.app.state.channel_manager
    existing = manager.get_channel("matrix")
    if existing and existing.is_connected:
        await existing.disconnect()

    from agntspace.channels.matrix import MatrixChannel
    matrix = MatrixChannel(homeserver, user_id, access_token)
    try:
        manager.register(matrix)
        await matrix.connect()
        return {"ok": True, "user_id": user_id}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/matrix/disconnect")
async def matrix_disconnect(request: Request) -> dict:
    """Disconnect from Matrix."""
    channel = request.app.state.channel_manager.get_channel("matrix")
    if not channel:
        return {"ok": False, "error": t("api.channels.matrix_not_connected")}
    await channel.disconnect()
    return {"ok": True}


@router.get("/whatsapp/status")
async def whatsapp_status(request: Request) -> dict:
    """Get WhatsApp connection status and QR availability."""
    channel = request.app.state.channel_manager.get_channel("whatsapp")
    if not channel:
        return {"connected": False, "error": t("api.channels.whatsapp_not_configured"), "bridge": "not registered"}
    return await channel.get_status()


@router.get("/whatsapp/qr")
async def whatsapp_qr(request: Request) -> dict:
    """Get QR code for WhatsApp pairing."""
    channel = request.app.state.channel_manager.get_channel("whatsapp")
    if not channel:
        return {"qr": None, "error": t("api.channels.whatsapp_not_configured")}
    qr = await channel.get_qr_code()
    return {"qr": qr}


@router.post("/whatsapp/connect")
async def whatsapp_connect(request: Request) -> dict:
    """Start the WhatsApp bridge and begin pairing."""
    from fastapi import HTTPException

    channel = request.app.state.channel_manager.get_channel("whatsapp")
    if not channel:
        from agntspace.channels.whatsapp import WhatsAppChannel

        channel = WhatsAppChannel()
        channel.set_db(request.app.state.db)
        request.app.state.channel_manager.register(channel)
        channel.on_message(request.app.state._whatsapp_handler)

    try:
        await channel.connect()
    except ConnectionError as e:
        raise HTTPException(status_code=500, detail=str(e)) from e
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=t("api.channels.whatsapp_bridge_failed", error=str(e)),
        ) from e
    return await channel.get_status()


@router.post("/whatsapp/disconnect")
async def whatsapp_disconnect(request: Request) -> dict:
    """Stop the WhatsApp bridge."""
    channel = request.app.state.channel_manager.get_channel("whatsapp")
    if channel:
        await channel.disconnect()
    return {"connected": False}


@router.post("/whatsapp/mode")
async def whatsapp_set_mode(request: Request) -> dict:
    """Toggle WhatsApp answering mode (personal/dedicated)."""
    channel = request.app.state.channel_manager.get_channel("whatsapp")
    if not channel:
        return {"error": t("api.channels.whatsapp_not_connected")}
    body = await request.json()
    enabled = body.get("enabled", False)
    mode = "dedicated" if enabled else "personal"
    new_mode = await channel.set_mode(mode)
    return {"mode": new_mode, "answering": new_mode == "dedicated"}


@router.post("/whatsapp/incoming")
async def whatsapp_incoming(request: Request) -> dict:
    """Receive incoming WhatsApp message from the bridge."""
    channel = request.app.state.channel_manager.get_channel("whatsapp")
    if not channel:
        return {"error": t("api.channels.whatsapp_not_configured")}
    payload = await request.json()
    try:
        reply = await channel.handle_incoming(payload)
    except Exception as exc:
        from agntspace.llm.base import LLMError

        if isinstance(exc, LLMError):
            log.error("WhatsApp LLM error: %s", exc.user_message)
            return {"reply": f"⚠ {exc.user_message}"}
        log.exception("WhatsApp incoming handler failed")
        return {"reply": f"⚠ {t('api.channels.whatsapp_error', error=str(exc))}"[:200]}
    return {"reply": reply}


@router.get("/messenger/webhook")
async def messenger_verify(request: Request) -> dict | str:
    """Handle Meta webhook verification (GET with challenge)."""
    channel = request.app.state.channel_manager.get_channel("messenger")
    if not channel:
        return {"ok": False, "error": t("api.channels.messenger_not_configured")}
    mode = request.query_params.get("hub.mode", "")
    token = request.query_params.get("hub.verify_token", "")
    challenge = request.query_params.get("hub.challenge", "")
    result = channel.verify_webhook(mode, token, challenge)
    if result is not None:
        from fastapi.responses import PlainTextResponse
        return PlainTextResponse(result)
    from fastapi import HTTPException
    raise HTTPException(status_code=403, detail=t("api.channels.messenger_verify_failed"))


@router.post("/messenger/webhook")
async def messenger_webhook(request: Request) -> dict:
    """Receive Messenger webhook events from Meta."""
    channel = request.app.state.channel_manager.get_channel("messenger")
    if not channel:
        return {"ok": False, "error": t("api.channels.messenger_not_configured")}
    payload = await request.json()
    response = await channel.handle_webhook(payload)
    return {"ok": True, "response": response is not None}


@router.post("/messenger/connect")
async def messenger_connect(request: Request) -> dict:
    """Connect to Facebook Messenger with a Page Access Token."""
    body = await request.json()
    page_token = body.get("page_access_token", "")
    verify_token = body.get("verify_token", "")
    if not page_token:
        return {"ok": False, "error": t("api.channels.messenger_token_missing")}

    manager = request.app.state.channel_manager
    existing = manager.get_channel("messenger")
    if existing and existing.is_connected:
        await existing.disconnect()

    from agntspace.channels.messenger import MessengerChannel
    messenger = MessengerChannel(page_token, verify_token)
    try:
        manager.register(messenger)
        await messenger.connect()
        return {"ok": True}
    except Exception as e:
        return {"ok": False, "error": str(e)}


@router.post("/messenger/disconnect")
async def messenger_disconnect(request: Request) -> dict:
    """Disconnect from Messenger."""
    channel = request.app.state.channel_manager.get_channel("messenger")
    if not channel:
        return {"ok": False, "error": t("api.channels.messenger_not_connected")}
    await channel.disconnect()
    return {"ok": True}


@router.post("/broadcast")
async def broadcast(request: Request, message: str) -> dict:
    """Broadcast a notification to all connected channels."""
    manager = request.app.state.channel_manager
    await manager.broadcast(message)
    return {"status": "ok", "channels": manager.connected_count}


# Dynamic route — must be LAST to avoid matching specific channel routes
@router.post("/{channel_name}/test")
async def test_channel(channel_name: str, request: Request) -> dict:
    """Send a test message to verify the channel works.

    Resolves the chat ID automatically:
    - WhatsApp: fetches self-number from bridge status → self-chat
    - Telegram: calls getUpdates to find the most recent chat
    - Others: uses the last known chat_id from incoming messages
    """
    manager = request.app.state.channel_manager
    channel = manager.get_channel(channel_name)
    if not channel:
        return {"ok": False, "error": f"{channel_name} is not registered"}
    if not channel.is_connected:
        return {"ok": False, "error": f"{channel_name} is not connected"}

    chat_id = getattr(channel, "_chat_id", None)

    # WhatsApp: derive self-chat from bridge status
    if not chat_id and channel_name == "whatsapp":
        try:
            import httpx as _httpx

            async with _httpx.AsyncClient(timeout=5) as client:
                resp = await client.get("http://localhost:8787/status")
                data = resp.json()
                self_number = data.get("self", "")
                if self_number:
                    chat_id = f"{self_number}@s.whatsapp.net"
        except Exception:
            pass

    # Telegram: poll getUpdates for the most recent chat
    if not chat_id and channel_name == "telegram":
        try:
            import httpx as _httpx

            base = getattr(channel, "_base_url", "")
            if base:
                async with _httpx.AsyncClient(timeout=10) as client:
                    resp = await client.get(f"{base}/getUpdates", params={"limit": 1, "offset": -1})
                    data = resp.json()
                    results = data.get("result", [])
                    if results:
                        msg = results[0].get("message", {})
                        cid = msg.get("chat", {}).get("id")
                        if cid:
                            chat_id = str(cid)
                            channel._chat_id = chat_id  # cache for next time
        except Exception:
            pass

    if not chat_id:
        hint = (
            "Open Telegram and send any message to your bot first."
            if channel_name == "telegram"
            else "Send a message to the bot first, then try again."
        )
        return {"ok": False, "error": hint}

    try:
        await channel.send_message(chat_id, "AgntSpace test — your channel is working.")
        return {"ok": True, "chat_id": chat_id}
    except Exception as e:
        return {"ok": False, "error": str(e)}
