"""Notion connector — search, query databases, and create pages via the Notion API.

Page creation and block appends write to the workspace, so they are sensitive and
gated by the policy engine; search and database queries are read-only.
"""

from __future__ import annotations

import httpx

from legate.agent.tooling import ToolContext, ToolResult, ToolSpec
from legate.connectors.base import BaseConnector, ConnectionResult
from legate.connectors.runtime import new_async_client

_API_BASE = "https://api.notion.com/v1"
_DEFAULT_VERSION = "2022-06-28"
_MAX_ITEMS = 50


class NotionConnector(BaseConnector):
    type = "notion"
    title = "Notion"
    config_schema = {
        "type": "object",
        "properties": {
            "default_database_id": {"type": "string"},
            "title_property": {
                "type": "string",
                "description": "Title column name (default 'Name').",
            },
            "notion_version": {"type": "string"},
        },
    }
    secret_schema = {
        "type": "object",
        "properties": {"token": {"type": "string", "description": "Notion integration token."}},
        "required": ["token"],
    }

    def list_tools(self) -> list[ToolSpec]:
        return [
            ToolSpec(
                name="notion.search",
                description="Search pages and databases the integration can access.",
                parameters={
                    "type": "object",
                    "properties": {
                        "query": {"type": "string"},
                        "limit": {"type": "integer"},
                    },
                },
                is_sensitive=False,
                scopes=["notion:read"],
            ),
            ToolSpec(
                name="notion.query_database",
                description="Query rows from a Notion database.",
                parameters={
                    "type": "object",
                    "properties": {
                        "database_id": {"type": "string"},
                        "limit": {"type": "integer"},
                    },
                },
                is_sensitive=False,
                scopes=["notion:read"],
            ),
            ToolSpec(
                name="notion.create_page",
                description="Create a page (a row) in a Notion database.",
                parameters={
                    "type": "object",
                    "properties": {
                        "database_id": {"type": "string"},
                        "title": {"type": "string"},
                        "content": {"type": "string", "description": "Optional page body text."},
                    },
                    "required": ["title"],
                },
                is_sensitive=True,
                scopes=["notion:write"],
            ),
            ToolSpec(
                name="notion.append_block",
                description="Append a paragraph of text to an existing page.",
                parameters={
                    "type": "object",
                    "properties": {
                        "page_id": {"type": "string"},
                        "text": {"type": "string"},
                    },
                    "required": ["page_id", "text"],
                },
                is_sensitive=True,
                scopes=["notion:write"],
            ),
        ]

    def _headers(self, config: dict, secrets: dict) -> dict:
        return {
            "Authorization": f"Bearer {secrets.get('token', '')}",
            "Notion-Version": config.get("notion_version") or _DEFAULT_VERSION,
            "Content-Type": "application/json",
        }

    async def _request(
        self, method: str, path: str, config: dict, secrets: dict, *, json: dict | None = None
    ) -> httpx.Response:
        async with new_async_client() as client:
            return await client.request(
                method, f"{_API_BASE}{path}", headers=self._headers(config, secrets), json=json
            )

    @staticmethod
    def _paragraph_block(text: str) -> dict:
        return {
            "object": "block",
            "type": "paragraph",
            "paragraph": {"rich_text": [{"type": "text", "text": {"content": text}}]},
        }

    async def test_connection(self, config: dict, secrets: dict) -> ConnectionResult:
        try:
            resp = await self._request("GET", "/users/me", config, secrets)
        except httpx.HTTPError as exc:
            return ConnectionResult(ok=False, message=f"connection failed: {exc}")
        if resp.status_code == 200:
            return ConnectionResult(ok=True, message="Notion integration authenticated")
        return ConnectionResult(ok=False, message=f"HTTP {resp.status_code}")

    async def execute(
        self, tool: str, args: dict, config: dict, secrets: dict, ctx: ToolContext
    ) -> ToolResult:
        if not secrets.get("token"):
            return ToolResult(ok=False, error="missing token")
        limit = min(int(args.get("limit", 20)), _MAX_ITEMS)

        try:
            if tool == "notion.search":
                body = {"page_size": limit}
                if args.get("query"):
                    body["query"] = args["query"]
                resp = await self._request("POST", "/search", config, secrets, json=body)
                if resp.status_code != 200:
                    return ToolResult(ok=False, error=f"HTTP {resp.status_code}")
                results = [
                    {"id": r.get("id"), "object": r.get("object"), "url": r.get("url")}
                    for r in resp.json().get("results", [])
                ]
                return ToolResult(ok=True, data={"results": results})

            if tool == "notion.query_database":
                database_id = args.get("database_id") or config.get("default_database_id")
                if not database_id:
                    return ToolResult(ok=False, error="no database_id specified")
                resp = await self._request(
                    "POST",
                    f"/databases/{database_id}/query",
                    config,
                    secrets,
                    json={"page_size": limit},
                )
                if resp.status_code != 200:
                    return ToolResult(ok=False, error=f"HTTP {resp.status_code}")
                rows = [
                    {"id": r.get("id"), "url": r.get("url")} for r in resp.json().get("results", [])
                ]
                return ToolResult(ok=True, data={"rows": rows})

            if tool == "notion.create_page":
                database_id = args.get("database_id") or config.get("default_database_id")
                if not database_id:
                    return ToolResult(ok=False, error="no database_id specified")
                title_prop = config.get("title_property") or "Name"
                payload: dict = {
                    "parent": {"database_id": database_id},
                    "properties": {
                        title_prop: {"title": [{"text": {"content": args.get("title", "")}}]}
                    },
                }
                if args.get("content"):
                    payload["children"] = [self._paragraph_block(args["content"])]
                resp = await self._request("POST", "/pages", config, secrets, json=payload)
                if resp.status_code not in (200, 201):
                    return ToolResult(ok=False, error=f"HTTP {resp.status_code}")
                return ToolResult(
                    ok=True, data={"id": resp.json().get("id"), "url": resp.json().get("url")}
                )

            if tool == "notion.append_block":
                resp = await self._request(
                    "PATCH",
                    f"/blocks/{args['page_id']}/children",
                    config,
                    secrets,
                    json={"children": [self._paragraph_block(args.get("text", ""))]},
                )
                if resp.status_code != 200:
                    return ToolResult(ok=False, error=f"HTTP {resp.status_code}")
                return ToolResult(ok=True, data={"appended": True, "page_id": args["page_id"]})

            return ToolResult(ok=False, error=f"unknown notion tool: {tool}")
        except httpx.HTTPError as exc:
            return ToolResult(ok=False, error=f"notion request failed: {exc}")
