"""Matrix bot management API routes."""

import logging

from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from notify_bridge_core.notifications.ssrf import (
    UnsafeURLError,
    avalidate_outbound_url,
)

from ..auth.dependencies import get_current_user
from ..database.engine import get_session
from ..database.models import MatrixBot, User
from .helpers import get_owned_entity

_LOGGER = logging.getLogger(__name__)

router = APIRouter(prefix="/api/matrix-bots", tags=["matrix-bots"])


class MatrixBotCreate(BaseModel):
    name: str
    icon: str = ""
    homeserver_url: str
    access_token: str
    display_name: str = ""


class MatrixBotUpdate(BaseModel):
    name: str | None = None
    icon: str | None = None
    homeserver_url: str | None = None
    access_token: str | None = None
    display_name: str | None = None


def _is_masked_secret(value: str | None) -> bool:
    """True when a field still carries our masked placeholder."""
    return bool(value) and (value.startswith("***") or "..." in value)


async def _validate_homeserver_url(url: str) -> None:
    """Reject homeserver URLs that point to blocked networks."""
    try:
        await avalidate_outbound_url(url)
    except UnsafeURLError as err:
        raise HTTPException(
            status_code=400, detail=f"Invalid homeserver_url: {err}"
        ) from err


@router.get("")
async def list_matrix_bots(
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    result = await session.exec(
        select(MatrixBot).where(MatrixBot.user_id == user.id)
    )
    return [_response(b) for b in result.all()]


@router.post("", status_code=status.HTTP_201_CREATED)
async def create_matrix_bot(
    body: MatrixBotCreate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    await _validate_homeserver_url(body.homeserver_url)
    bot = MatrixBot(user_id=user.id, **body.model_dump())
    session.add(bot)
    await session.commit()
    await session.refresh(bot)
    return _response(bot)


@router.get("/{bot_id}")
async def get_matrix_bot(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    return _response(await _get_user_bot(session, bot_id, user.id))


@router.put("/{bot_id}")
async def update_matrix_bot(
    bot_id: int,
    body: MatrixBotUpdate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    bot = await _get_user_bot(session, bot_id, user.id)
    updates = body.model_dump(exclude_unset=True)

    # Re-validate homeserver_url whenever the client supplies a new one so
    # no private/loopback target can ever be saved, even via update.
    if "homeserver_url" in updates and updates["homeserver_url"]:
        await _validate_homeserver_url(updates["homeserver_url"])

    # Never accept the masked placeholder the GET response returns. If the
    # client echoes it back, keep the stored secret.
    if "access_token" in updates and _is_masked_secret(updates["access_token"]):
        updates.pop("access_token")

    for field, value in updates.items():
        setattr(bot, field, value)
    session.add(bot)
    await session.commit()
    await session.refresh(bot)
    return _response(bot)


@router.delete("/{bot_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_matrix_bot(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    from .delete_protection import check_matrix_bot, raise_if_used
    bot = await _get_user_bot(session, bot_id, user.id)
    raise_if_used(await check_matrix_bot(session, bot.id), bot.name)
    await session.delete(bot)
    await session.commit()


@router.post("/{bot_id}/test")
async def test_matrix_bot(
    bot_id: int,
    room_id: str = "",
    locale: str = Query("en"),
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Test Matrix bot connection by sending a message to a room.

    If room_id is not provided, just verifies the access token by calling /whoami.
    """
    bot = await _get_user_bot(session, bot_id, user.id)
    # Defense-in-depth: even though create/update validate the URL, a bot row
    # written before this guard was added could still point at a blocked host.
    await _validate_homeserver_url(bot.homeserver_url)

    import aiohttp
    from ..services.http_session import get_http_session
    http = await get_http_session()
    whoami_url = f"{bot.homeserver_url.rstrip('/')}/_matrix/client/v3/account/whoami"
    headers = {"Authorization": f"Bearer {bot.access_token}"}
    try:
        async with http.get(whoami_url, headers=headers, allow_redirects=False) as resp:
            if resp.status != 200:
                body = await resp.text()
                return {"success": False, "error": f"Auth failed: HTTP {resp.status} — {body[:200]}"}
            whoami = await resp.json()
    except aiohttp.ClientError as e:
        return {"success": False, "error": f"Connection failed: {e}"}

    result = {"success": True, "user_id": whoami.get("user_id", "")}

    if room_id:
        from ..services.notifier import _get_test_message
        from notify_bridge_core.notifications.matrix.client import MatrixClient
        msg = _get_test_message(locale, "matrix")
        client = MatrixClient(http, bot.homeserver_url, bot.access_token)
        send_result = await client.send_message(
            room_id,
            msg,
            html_message=f"<b>{msg}</b>",
        )
        result["send_result"] = send_result

    return result


def _response(bot: MatrixBot) -> dict:
    return {
        "id": bot.id,
        "name": bot.name,
        "icon": bot.icon,
        "homeserver_url": bot.homeserver_url,
        "access_token": f"***{bot.access_token[-4:]}" if len(bot.access_token) > 4 else "***",
        "display_name": bot.display_name,
        "created_at": bot.created_at.isoformat(),
    }


async def _get_user_bot(session: AsyncSession, bot_id: int, user_id: int) -> MatrixBot:
    return await get_owned_entity(
        session, MatrixBot, bot_id, user_id, not_found_msg="Matrix bot not found",
    )
