# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""External connection tools (list connections, upload, send mail)."""

import logging
from typing import Any, Dict, List, Optional

from modules.serviceCenter.services.serviceAgent.datamodelAgent import ToolResult
from modules.serviceCenter.services.serviceAgent.toolRegistry import ToolRegistry

from modules.serviceCenter.services.serviceAgent.coreTools._helpers import (
    _buildResolverDbFromServices,
    _getOrCreateTempFolder,
    _looksLikeBinary,
    _resolveFileScope,
    _MAX_TOOL_RESULT_CHARS,
)

logger = logging.getLogger(__name__)


def _registerConnectionTools(registry: ToolRegistry, services):
    """Auto-extracted from registerCoreTools."""
    # ---- Connection tools (external data sources) ----

    async def _listConnections(args: Dict[str, Any], context: Dict[str, Any]):
        try:
            chatService = services.chat
            connections = chatService.getUserConnections() if hasattr(chatService, "getUserConnections") else []
            if not connections:
                return ToolResult(toolCallId="", toolName="listConnections", success=True, data="No connections available.")
            lines = []
            for conn in connections:
                connId = conn.get("id", "") if isinstance(conn, dict) else getattr(conn, "id", "")
                authority = conn.get("authority", "?") if isinstance(conn, dict) else getattr(conn, "authority", "?")
                authorityVal = authority.value if hasattr(authority, "value") else str(authority)
                username = conn.get("externalUsername", "") if isinstance(conn, dict) else getattr(conn, "externalUsername", "")
                email = conn.get("externalEmail", "") if isinstance(conn, dict) else getattr(conn, "externalEmail", "")
                cid = conn.get("id", "") if isinstance(conn, dict) else getattr(conn, "id", "")
                ref = f"connection:{authorityVal}:{username}"
                lines.append(
                    f"- {ref}  connectionId={cid}  ({email})  "
                    f"(use this full connection: line or connectionId as connectionReference)"
                )
            return ToolResult(toolCallId="", toolName="listConnections", success=True, data="\n".join(lines))
        except Exception as e:
            return ToolResult(toolCallId="", toolName="listConnections", success=False, error=str(e))

    async def _uploadToExternal(args: Dict[str, Any], context: Dict[str, Any]):
        connectionId = args.get("connectionId", "")
        service = args.get("service", "")
        path = args.get("path", "")
        fileId = args.get("fileId", "")
        if not connectionId or not service or not path or not fileId:
            return ToolResult(toolCallId="", toolName="uploadToExternal", success=False, error="connectionId, service, path, and fileId are required")
        try:
            from modules.connectors.connectorResolver import ConnectorResolver
            resolver = ConnectorResolver(
                services.getService("security"),
                _buildResolverDbFromServices(services),
            )
            adapter = await resolver.resolveService(connectionId, service)
            chatService = services.chat
            fileContent = chatService.getFileContent(fileId)
            if not fileContent:
                return ToolResult(toolCallId="", toolName="uploadToExternal", success=False, error="File not found")
            fileData = fileContent.get("data", b"") if isinstance(fileContent, dict) else b""
            if isinstance(fileData, str):
                fileData = fileData.encode("utf-8")
            fileName = fileContent.get("fileName", "file") if isinstance(fileContent, dict) else "file"
            result = await adapter.upload(path, fileData, fileName)
            return ToolResult(toolCallId="", toolName="uploadToExternal", success=True, data=str(result))
        except Exception as e:
            return ToolResult(toolCallId="", toolName="uploadToExternal", success=False, error=str(e))

    async def _sendMail(args: Dict[str, Any], context: Dict[str, Any]):
        import base64 as _b64

        connectionId = args.get("connectionId", "")
        to = args.get("to", [])
        subject = args.get("subject", "")
        body = args.get("body", "")
        bodyType = "HTML" if args.get("bodyType", "text").lower() == "html" else "Text"
        draft = args.get("draft", False)
        attachmentFileIds = args.get("attachmentFileIds") or []

        if not connectionId or not to or not subject:
            return ToolResult(toolCallId="", toolName="sendMail", success=False, error="connectionId, to, and subject are required")
        try:
            graphAttachments: List[Dict[str, Any]] = []
            if attachmentFileIds:
                chatService = services.chat
                dbMgmt = chatService.interfaceDbComponent
                for fid in attachmentFileIds:
                    fileRow = dbMgmt.getFile(fid)
                    if not fileRow:
                        return ToolResult(toolCallId="", toolName="sendMail", success=False, error=f"Attachment file not found: {fid}")
                    rawBytes = dbMgmt.getFileData(fid)
                    if not rawBytes:
                        return ToolResult(toolCallId="", toolName="sendMail", success=False, error=f"Attachment file has no data: {fid}")
                    graphAttachments.append({
                        "name": fileRow.fileName,
                        "contentBytes": _b64.b64encode(rawBytes).decode("ascii"),
                        "contentType": getattr(fileRow, "mimeType", "application/octet-stream"),
                    })

            from modules.connectors.connectorResolver import ConnectorResolver
            resolver = ConnectorResolver(
                services.getService("security"),
                _buildResolverDbFromServices(services),
            )
            adapter = await resolver.resolveService(connectionId, "outlook")

            if draft and hasattr(adapter, "createDraft"):
                result = await adapter.createDraft(
                    to=to, subject=subject, body=body, bodyType=bodyType,
                    cc=args.get("cc"), attachments=graphAttachments or None,
                )
                return ToolResult(toolCallId="", toolName="sendMail", success=True, data=str(result))

            if hasattr(adapter, "sendMail"):
                result = await adapter.sendMail(
                    to=to, subject=subject, body=body, bodyType=bodyType,
                    cc=args.get("cc"), attachments=graphAttachments or None,
                )
                return ToolResult(toolCallId="", toolName="sendMail", success=True, data=str(result))
            return ToolResult(toolCallId="", toolName="sendMail", success=False, error="Mail not supported by this adapter")
        except Exception as e:
            return ToolResult(toolCallId="", toolName="sendMail", success=False, error=str(e))

    _connToolParams = {
        "connectionId": {"type": "string", "description": "UserConnection UUID (from listConnections output, e.g. '3fa85f64-...')"},
        "service": {"type": "string", "description": "Service name (sharepoint, outlook, drive, etc.)"},
    }

    registry.register(
        "listConnections", _listConnections,
        description="List the user's external connections (SharePoint, OneDrive, Outlook, etc.) and their IDs. Use with browseDataSource/uploadToExternal.",
        parameters={"type": "object", "properties": {}},
        readOnly=True,
    )

    registry.register(
        "uploadToExternal", _uploadToExternal,
        description=(
            "Upload a local file to an external storage via connectionId+service. "
            "Use listConnections to find available connections."
        ),
        parameters={
            "type": "object",
            "properties": {
                **_connToolParams,
                "path": {"type": "string", "description": "Destination path on the external service"},
                "fileId": {"type": "string", "description": "Local file ID to upload"},
            },
            "required": ["connectionId", "service", "path", "fileId"],
        },
        readOnly=False,
    )

    registry.register(
        "sendMail", _sendMail,
        description=(
            "Send or draft an email via a connected mail service (Outlook). "
            "Supports HTML body and file attachments from the workspace. "
            "Set draft=true to save as draft without sending. "
            "Use listConnections to find the connectionId."
        ),
        parameters={
            "type": "object",
            "properties": {
                "connectionId": {"type": "string", "description": "UserConnection UUID (from listConnections output)"},
                "to": {"type": "array", "items": {"type": "string"}, "description": "Recipient email addresses"},
                "subject": {"type": "string", "description": "Email subject"},
                "body": {"type": "string", "description": "Email body — plain text or HTML markup"},
                "bodyType": {"type": "string", "enum": ["text", "html"], "description": "Body format: 'text' (default) or 'html'"},
                "cc": {"type": "array", "items": {"type": "string"}, "description": "CC addresses"},
                "attachmentFileIds": {
                    "type": "array", "items": {"type": "string"},
                    "description": "File IDs from the workspace to attach (use listFiles to find IDs)",
                },
                "draft": {"type": "boolean", "description": "If true, save as draft in Drafts folder instead of sending"},
            },
            "required": ["connectionId", "to", "subject", "body"],
        },
        readOnly=False,
    )
