"""Word (.docx) file operations via python-docx."""

import os

from fliiq.runtime.security import check_path_allowed

MAX_ELEMENTS = 5_000


async def handler(params: dict) -> dict:
    """Handle Word document operations."""
    action = params["action"]

    if action == "create":
        return _create(params)
    elif action == "read":
        return _read(params)
    elif action == "add_content":
        return _add_content(params)
    elif action == "replace_text":
        return _replace_text(params)
    elif action == "delete":
        return _delete(params)
    else:
        raise ValueError(f"Unknown action: {action}")


def _require_path(params: dict) -> str:
    path = params.get("path")
    if not path:
        raise ValueError("'path' is required")
    check_path_allowed(path)
    return path


def _create(params: dict) -> dict:
    """Create a new .docx document."""
    from docx import Document

    path = _require_path(params)
    text = params.get("text")

    doc = Document()

    if text:
        doc.add_paragraph(text)

    parent = os.path.dirname(path)
    if parent:
        os.makedirs(parent, exist_ok=True)
    doc.save(path)

    return {
        "success": True,
        "message": f"Created {path}",
        "data": {"path": path},
    }


def _read(params: dict) -> dict:
    """Read document content as structured elements."""
    from docx import Document

    path = _require_path(params)
    if not os.path.isfile(path):
        raise FileNotFoundError(f"File not found: {path}")

    doc = Document(path)
    elements = []
    count = 0

    for paragraph in doc.paragraphs:
        if count >= MAX_ELEMENTS:
            break
        text = paragraph.text.strip()
        if not text:
            continue
        style_name = paragraph.style.name if paragraph.style else ""
        if style_name.startswith("Heading"):
            try:
                level = int(style_name.replace("Heading ", ""))
            except ValueError:
                level = 1
            elements.append({"type": "heading", "level": level, "text": paragraph.text})
        else:
            elements.append({"type": "paragraph", "text": paragraph.text})
        count += 1

    for table in doc.tables:
        if count >= MAX_ELEMENTS:
            break
        rows = []
        for row in table.rows:
            rows.append([cell.text for cell in row.cells])
        elements.append({"type": "table", "rows": len(rows), "data": rows})
        count += 1

    truncated = count >= MAX_ELEMENTS
    return {
        "success": True,
        "message": f"Read {len(elements)} element(s) from {path}" + (" (truncated)" if truncated else ""),
        "data": {
            "path": path,
            "elements": elements,
            "truncated": truncated,
        },
    }


def _add_content(params: dict) -> dict:
    """Append content to the document."""
    from docx import Document

    path = _require_path(params)
    if not os.path.isfile(path):
        raise FileNotFoundError(f"File not found: {path}")

    content_type = params.get("content_type", "paragraph")
    doc = Document(path)

    if content_type == "heading":
        text = params.get("text")
        if not text:
            raise ValueError("'text' is required for heading")
        level = params.get("level", 1)
        if level < 1 or level > 4:
            raise ValueError("Heading level must be 1-4")
        doc.add_heading(text, level=level)
        msg = f"Added heading (level {level}): '{text}'"

    elif content_type == "paragraph":
        text = params.get("text")
        if not text:
            raise ValueError("'text' is required for paragraph")
        doc.add_paragraph(text)
        msg = f"Added paragraph ({len(text)} chars)"

    elif content_type == "table":
        data = params.get("data")
        if not data or len(data) < 1:
            raise ValueError("'data' (2D array) is required for table, minimum 1 row")
        rows = len(data)
        cols = len(data[0])
        table = doc.add_table(rows=rows, cols=cols, style="Table Grid")
        for r_idx, row in enumerate(data):
            for c_idx, value in enumerate(row):
                if c_idx < cols:
                    table.cell(r_idx, c_idx).text = str(value) if value is not None else ""
        msg = f"Added table ({rows}x{cols})"

    else:
        raise ValueError(f"Unknown content_type: {content_type}. Use: heading, paragraph, table")

    doc.save(path)

    return {
        "success": True,
        "message": msg,
        "data": {"path": path, "content_type": content_type},
    }


def _replace_text(params: dict) -> dict:
    """Find and replace text throughout the document."""
    from docx import Document

    path = _require_path(params)
    if not os.path.isfile(path):
        raise FileNotFoundError(f"File not found: {path}")

    old_text = params.get("old_text")
    new_text = params.get("new_text")
    if not old_text or new_text is None:
        raise ValueError("'old_text' and 'new_text' are required for replace_text")

    doc = Document(path)
    replacements = 0

    for paragraph in doc.paragraphs:
        if old_text in paragraph.text:
            for run in paragraph.runs:
                if old_text in run.text:
                    run.text = run.text.replace(old_text, new_text)
                    replacements += 1

    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for paragraph in cell.paragraphs:
                    if old_text in paragraph.text:
                        for run in paragraph.runs:
                            if old_text in run.text:
                                run.text = run.text.replace(old_text, new_text)
                                replacements += 1

    if replacements == 0:
        raise ValueError(f"Text '{old_text}' not found in {path}")

    doc.save(path)

    return {
        "success": True,
        "message": f"Replaced {replacements} occurrence(s) of '{old_text}'",
        "data": {"path": path, "replacements": replacements},
    }


def _delete(params: dict) -> dict:
    """Delete a .docx file."""
    path = _require_path(params)
    if not os.path.isfile(path):
        raise FileNotFoundError(f"File not found: {path}")
    os.remove(path)
    return {
        "success": True,
        "message": f"Deleted {path}",
        "data": {"deleted": path},
    }
