"""
Word document sink provider for g-gremlin.

Implements the DocsSinkProvider protocol for local .docx file output.
Designed for Microsoft 365 shops - save to OneDrive/SharePoint sync folders.

Usage:
    provider = WordSinkProvider(output_dir=Path("./docs"))
    result = provider.upsert_page(DocsPage(
        external_id="my-doc",
        title="My Document",
        content_markdown="# Hello\\n\\nWorld",
        parent_id=None,
    ))
    # Result: ./docs/my-doc.docx

Requires: pip install python-docx (or: pip install g-gremlin[docs-word])
"""

from __future__ import annotations

import re
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional

from g_gremlin.contracts import BatchOutcome
from g_gremlin.docs_sink import DocsPage, DocsProvider, DocsPublishResult
from g_gremlin.word_exporter import WordExporter


def _slugify(s: str, max_length: int = 120) -> str:
    """Convert a string to a safe filename slug."""
    s = s.strip().lower()
    # Replace non-alphanumeric chars with hyphens
    s = re.sub(r"[^a-z0-9_-]", "-", s)
    # Collapse multiple hyphens
    s = re.sub(r"-+", "-", s)
    # Strip leading/trailing hyphens
    s = s.strip("-")
    return s[:max_length] if s else "untitled"


@dataclass
class WordSinkProvider:
    """
    DocsSinkProvider implementation for local Word document output.

    Writes .docx files to a configurable output directory.
    Files are named by external_id (slugified) or title.

    Example:
        provider = WordSinkProvider(output_dir=Path("~/OneDrive/FoundryDocs"))
        provider.upsert_page(page)  # Creates ~/OneDrive/FoundryDocs/{external_id}.docx
    """

    output_dir: Path
    exporter: WordExporter = None  # type: ignore[assignment]

    def __post_init__(self) -> None:
        if self.exporter is None:
            self.exporter = WordExporter()

    @property
    def name(self) -> str:
        return "word"

    @property
    def provider_type(self) -> DocsProvider:
        return DocsProvider.WORD

    def test_connection(self) -> Dict[str, Any]:
        """
        Verify the output directory exists and is writable.

        Returns:
            Dict with status and directory info
        """
        try:
            # Ensure directory exists
            self.output_dir.mkdir(parents=True, exist_ok=True)

            # Test write access
            test_file = self.output_dir / ".g-gremlin-test"
            test_file.write_text("test")
            test_file.unlink()

            return {
                "status": "ok",
                "output_dir": str(self.output_dir.resolve()),
                "writable": True,
            }
        except PermissionError as exc:
            return {
                "status": "error",
                "output_dir": str(self.output_dir),
                "writable": False,
                "error": f"Permission denied: {exc}",
            }
        except Exception as exc:
            return {
                "status": "error",
                "output_dir": str(self.output_dir),
                "error": str(exc),
            }

    def upsert_page(self, page: DocsPage) -> DocsPublishResult:
        """
        Create or update a Word document.

        The document is named using external_id (slugified) or title.
        If the file already exists, it is overwritten (upsert semantics).

        Args:
            page: DocsPage with content_markdown to convert

        Returns:
            DocsPublishResult with file path and status
        """
        try:
            # Generate filename from external_id or title
            filename_base = _slugify(page.external_id or page.title)
            out_path = self.output_dir / f"{filename_base}.docx"

            # Determine if this is create or update
            is_update = out_path.exists()

            # Export to Word
            result = self.exporter.export(
                title=page.title,
                markdown=page.content_markdown or "",
                out_path=out_path,
            )

            if not result.success:
                return DocsPublishResult(
                    external_id=page.external_id,
                    status="failed",
                    page_id=None,
                    page_url=None,
                    error=result.error,
                )

            # Return success with file:// URL
            file_url = out_path.resolve().as_uri()

            return DocsPublishResult(
                external_id=page.external_id,
                status="updated" if is_update else "created",
                page_id=filename_base,
                page_url=file_url,
            )

        except Exception as exc:
            return DocsPublishResult(
                external_id=page.external_id,
                status="failed",
                page_id=None,
                page_url=None,
                error=str(exc),
            )

    def upsert_pages_batch(self, pages: List[DocsPage]) -> BatchOutcome:
        """
        Create or update multiple Word documents.

        Args:
            pages: List of DocsPage objects to export

        Returns:
            BatchOutcome with succeeded/failed/retryable lists
        """
        outcome = BatchOutcome()

        for page in pages:
            result = self.upsert_page(page)
            result_dict = result.to_dict()

            if result.status in ("created", "updated"):
                outcome.succeeded.append(result_dict)
            elif result.status == "failed":
                outcome.failed.append(result_dict)
            else:
                outcome.retryable_failed.append(result_dict)

        return outcome

    def get_page_by_external_id(self, external_id: str) -> Optional[Dict[str, Any]]:
        """
        Check if a document with the given external_id exists.

        Args:
            external_id: The external identifier to look up

        Returns:
            Dict with file info if found, None otherwise
        """
        filename_base = _slugify(external_id)
        file_path = self.output_dir / f"{filename_base}.docx"

        if not file_path.exists():
            return None

        stat = file_path.stat()
        return {
            "id": filename_base,
            "external_id": external_id,
            "path": str(file_path),
            "url": file_path.resolve().as_uri(),
            "size_bytes": stat.st_size,
            "modified_time": stat.st_mtime,
        }

    def delete_page(self, page_id: str) -> bool:
        """
        Delete a Word document by its page_id (filename slug).

        Args:
            page_id: The page identifier (filename without .docx)

        Returns:
            True if deleted, False if not found or error
        """
        try:
            file_path = self.output_dir / f"{page_id}.docx"
            if file_path.exists():
                file_path.unlink()
                return True
            return False
        except Exception:
            return False


__all__ = [
    "WordSinkProvider",
]
