"""Source tools - Source management with consolidated source_add."""

from ...services import ServiceError, ValidationError
from ...services import sources as sources_service
from ._utils import ResultDict, coerce_list, error_result, get_client, logged_tool


def _normalize_source_validation_error(message: str) -> str:
    """Preserve historical MCP wire wording for invalid source_type."""
    if message.startswith("Unknown source type "):
        return message.replace("Unknown source type", "Unknown source_type", 1)
    return message


@logged_tool()
def source_add(
    notebook_id: str,
    source_type: str,
    url: str | None = None,
    urls: list[str] | None = None,
    text: str | None = None,
    title: str | None = None,
    file_path: str | None = None,
    document_id: str | None = None,
    doc_type: str = "doc",
    wait: bool = False,
    wait_timeout: float = 120.0,
) -> ResultDict:
    """Add a source to a notebook. Unified tool for all source types.

    Supports: url, text, drive, file

    Args:
        notebook_id: Notebook UUID
        source_type: Type of source to add:
            - url: Web page or YouTube URL
            - text: Pasted text content
            - drive: Google Drive document
            - file: Local file upload. Supported extensions:
                PDF, TXT, MD, DOCX, CSV, EPUB, MP3, M4A, WAV, AAC, OGG,
                OPUS, MP4, JPG, JPEG, PNG, GIF, WEBP. Image-bearing
                sources (PDF / JPG / PNG / etc.) feed Studio video
                generation's visual-crop pipeline — charts, photos, and
                diagrams may be extracted as on-screen aids in Video
                Overviews.
        url: URL to add (for source_type=url)
        urls: List of URLs to add in bulk (for source_type=url, alternative to url)
        text: Text content to add (for source_type=text)
        title: Display title (for text sources)
        file_path: Local file path (for source_type=file)
        document_id: Google Drive document ID (for source_type=drive)
        doc_type: Drive doc type: doc|slides|sheets|pdf (for source_type=drive)
        wait: If True, wait for source processing to complete before returning
        wait_timeout: Max seconds to wait if wait=True (default 120)

    Example:
        source_add(notebook_id="abc", source_type="url", url="https://example.com")
        source_add(notebook_id="abc", source_type="url", urls=["https://a.com", "https://b.com"])
        source_add(notebook_id="abc", source_type="url", url="https://example.com", wait=True)
        source_add(notebook_id="abc", source_type="file", file_path="/path/to/doc.pdf", wait=True)
        source_add(notebook_id="abc", source_type="file", file_path="/path/to/screenshot.png", wait=True)
    """
    try:
        client = get_client()

        # Coerce list params from MCP clients (may arrive as strings)
        coerced_urls: list[str] | None = coerce_list(urls)

        # Bulk URL add: when urls list is provided
        if coerced_urls and source_type == "url":
            bulk_result = sources_service.add_sources(
                client,
                notebook_id,
                [{"source_type": "url", "url": url_value} for url_value in coerced_urls],
                wait=wait,
                wait_timeout=wait_timeout,
            )
            return {"status": "success", "ready": wait, **bulk_result}

        # Single source add (existing behavior)
        single_result = sources_service.add_source(
            client,
            notebook_id,
            source_type,
            url=url,
            text=text,
            title=title,
            file_path=file_path,
            document_id=document_id,
            doc_type=doc_type,
            wait=wait,
            wait_timeout=wait_timeout,
        )
        return {"status": "success", "ready": wait, **single_result}
    except ValidationError as e:
        return error_result(_normalize_source_validation_error(str(e)))
    except ServiceError as e:
        return error_result(e.user_message, hint=e.hint)
    except Exception as e:
        return error_result(str(e))


@logged_tool()
def source_list_drive(notebook_id: str) -> ResultDict:
    """List sources with types and Drive freshness status.

    Use before source_sync_drive to identify stale sources.

    Args:
        notebook_id: Notebook UUID
    """
    try:
        client = get_client()
        result = sources_service.list_drive_sources(client, notebook_id)
        return {"status": "success", "notebook_id": notebook_id, **result}
    except ServiceError as e:
        return error_result(e.user_message, hint=e.hint)
    except Exception as e:
        return error_result(str(e))


@logged_tool()
def source_sync_drive(source_ids: list[str], confirm: bool = False) -> ResultDict:
    """Sync Drive sources with latest content. Requires confirm=True.

    Call source_list_drive first to identify stale sources.

    Args:
        source_ids: Source UUIDs to sync
        confirm: Must be True after user approval
    """
    if not confirm:
        return error_result(
            "Sync not confirmed. Set confirm=True after user approval.",
            hint="Call source_list_drive first to see which sources are stale.",
        )

    try:
        client = get_client()
        # Coerce list params from MCP clients (may arrive as strings)
        coerced_source_ids: list[str] | None = coerce_list(source_ids)
        if not coerced_source_ids:
            return error_result("source_ids is required.")
        sync_results = sources_service.sync_drive_sources(client, coerced_source_ids)
        synced_count = sum(1 for item in sync_results if item.get("synced"))
        return {
            "status": "success",
            "synced_count": synced_count,
            "total_count": len(coerced_source_ids),
            "results": sync_results,
        }
    except ServiceError as e:
        return error_result(e.user_message, hint=e.hint)
    except Exception as e:
        return error_result(str(e))


@logged_tool()
def source_rename(notebook_id: str, source_id: str, new_title: str) -> ResultDict:
    """Rename a source in a notebook.

    Args:
        notebook_id: Notebook UUID containing the source
        source_id: Source UUID to rename
        new_title: New display title for the source
    """
    try:
        client = get_client()
        result = sources_service.rename_source(client, notebook_id, source_id, new_title)
        return {"status": "success", **result}
    except ServiceError as e:
        return error_result(e.user_message, hint=e.hint)
    except Exception as e:
        return error_result(str(e))


@logged_tool()
def source_delete(
    source_id: str | None = None,
    source_ids: list[str] | None = None,
    confirm: bool = False,
) -> ResultDict:
    """Delete source(s) permanently. IRREVERSIBLE. Requires confirm=True.

    Args:
        source_id: Source UUID to delete (single)
        source_ids: List of source UUIDs to delete (bulk, alternative to source_id)
        confirm: Must be True after user approval
    """
    if not confirm:
        return error_result(
            "Deletion not confirmed. Set confirm=True after user approval.",
            warning="This action is IRREVERSIBLE.",
        )

    try:
        client = get_client()

        # Coerce list params from MCP clients (may arrive as strings)
        coerced_source_ids: list[str] | None = coerce_list(source_ids)

        # Bulk delete: when source_ids list is provided
        if coerced_source_ids:
            sources_service.delete_sources(client, coerced_source_ids)
            return {
                "status": "success",
                "message": f"{len(coerced_source_ids)} sources have been permanently deleted.",
                "deleted_count": len(coerced_source_ids),
            }

        # Single delete (existing behavior)
        if not source_id:
            return error_result("Either source_id or source_ids is required.")

        sources_service.delete_source(client, source_id)
        return {
            "status": "success",
            "message": f"Source {source_id} has been permanently deleted.",
        }
    except ServiceError as e:
        return error_result(e.user_message, hint=e.hint)
    except Exception as e:
        return error_result(str(e))


@logged_tool()
def source_describe(source_id: str) -> ResultDict:
    """Get AI-generated source summary with keyword chips.

    Args:
        source_id: Source UUID

    Returns: summary (markdown with **bold** keywords), keywords list
    """
    try:
        client = get_client()
        result = sources_service.describe_source(client, source_id)
        return {"status": "success", **result}
    except ServiceError as e:
        return error_result(e.user_message, hint=e.hint)
    except Exception as e:
        return error_result(str(e))


@logged_tool()
def source_get_content(source_id: str) -> ResultDict:
    """Get raw text content of a source (no AI processing).

    Returns the original indexed text from PDFs, web pages, pasted text,
    or YouTube transcripts. Much faster than notebook_query for content export.

    Args:
        source_id: Source UUID

    Returns: content (str), title (str), source_type (str), char_count (int)
    """
    try:
        client = get_client()
        result = sources_service.get_source_content(client, source_id)
        return {"status": "success", **result}
    except ServiceError as e:
        return error_result(e.user_message, hint=e.hint)
    except Exception as e:
        return error_result(str(e))
