"""External editor integration for content part editing.

Opens the user's preferred $EDITOR to edit content,
with content-type-aware file extensions for syntax highlighting.
"""

import json
import subprocess
import tempfile
from pathlib import Path
from typing import Any, cast

from cli.infrastructure.editor import get_editor


class ContentValidationError(Exception):
    """Raised when edited content fails validation."""


__all__ = [
    "ContentValidationError",
    "open_editor_for_content",
    "format_content_for_editor",
    "parse_edited_content",
]


def open_editor_for_content(
    initial_content: str,
    is_structured: bool,
) -> str | None:
    """Open the user's editor with content, using appropriate file extension.

    Creates a temporary file with the right extension for syntax highlighting:
    - .json for structured content
    - .txt for text content

    Args:
        initial_content: Initial content to populate the file with
        is_structured: Whether the content is JSON (structured) or plain text

    Returns:
        The edited content from the file, or None if the editor failed
        or the user quit without saving.

    Raises:
        EditorError: If no editor is available
        ContentValidationError: If JSON content is malformed after editing
    """
    editor = get_editor()

    # Choose extension based on content type
    suffix = ".json" if is_structured else ".txt"

    # Create temp file with appropriate extension
    with tempfile.NamedTemporaryFile(
        mode="w",
        suffix=suffix,
        delete=False,
    ) as f:
        f.write(initial_content)
        temp_path = Path(f.name)

    try:
        # Get file modification time before editing
        mtime_before = temp_path.stat().st_mtime

        # Open editor and wait for it to close
        editor_parts = editor.split()
        cmd = editor_parts + [str(temp_path)]

        result = subprocess.call(cmd)

        if result != 0:
            return None

        # Check if file was modified
        mtime_after = temp_path.stat().st_mtime
        if mtime_after == mtime_before:
            # File wasn't modified - still return content in case
            # user just didn't change anything
            pass

        # Read the edited content
        content = temp_path.read_text()

        # Validate JSON if structured content
        if is_structured:
            try:
                json.loads(content)
            except json.JSONDecodeError as e:
                raise ContentValidationError(
                    f"Invalid JSON after editing: {e.msg} at line {e.lineno}"
                )

        return content

    finally:
        # Clean up temp file
        try:
            temp_path.unlink()
        except OSError:
            pass  # Best effort cleanup


def format_content_for_editor(
    content_text: str | None, content_structured: dict[str, Any] | None
) -> tuple[str, bool]:
    """Format content for editing in $EDITOR.

    Args:
        content_text: Plain text content (if text type)
        content_structured: Structured JSON content (if structured type)

    Returns:
        Tuple of (formatted_content, is_structured)
    """
    if content_structured is not None:
        # Format JSON with indentation for readability
        return json.dumps(content_structured, indent=2, ensure_ascii=False), True
    elif content_text is not None:
        return content_text, False
    else:
        return "", False


def parse_edited_content(
    edited_content: str,
    is_structured: bool,
) -> tuple[str | None, dict[str, Any] | None]:
    """Parse edited content back to appropriate type.

    Args:
        edited_content: Content string from editor
        is_structured: Whether content should be parsed as JSON

    Returns:
        Tuple of (content_text, content_structured) with appropriate field set

    Raises:
        ContentValidationError: If JSON parsing fails for structured content
    """
    if is_structured:
        try:
            parsed = json.loads(edited_content)
            return None, cast(dict[str, Any], parsed)
        except json.JSONDecodeError as e:
            raise ContentValidationError(f"Invalid JSON: {e.msg} at line {e.lineno}")
    else:
        return edited_content, None
