#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: "2025-01-19 14:25:00 (ywatanabe)"
# File: ./src/scitex/io/_save_modules/_bibtex.py
# ----------------------------------------
import os

__FILE__ = __file__
__DIR__ = os.path.dirname(__FILE__)
# ----------------------------------------

"""
BibTeX file saving module for SciTeX IO.

This module provides functionality to save data in BibTeX format,
supporting both individual entries and collections.
"""

try:
    from scitex import logging

    SCITEX_LOGGING_AVAILABLE = True
except ImportError:
    import logging

    SCITEX_LOGGING_AVAILABLE = False
from typing import List, Dict, Any, Union
from datetime import datetime

logger = logging.getLogger(__name__)


def save_bibtex(obj: Union[Dict, List[Dict]], spath: str, **kwargs) -> None:
    """
    Save data as BibTeX file.

    Args:
        obj: Can be:
            - A single BibTeX entry (dict)
            - List of BibTeX entries (list of dicts)
            - A Paper object from scitex.scholar
            - A PaperCollection object from scitex.scholar
        spath: Path where the .bib file will be saved
        **kwargs: Additional arguments
            - encoding: File encoding (default: utf-8)
            - add_header: Add header comment (default: True)
            - append: Append to existing file (default: False)
            - include_enriched: Include enriched metadata for Paper objects (default: True)

    The input dict should have structure:
    {
        'entry_type': 'article',
        'key': 'author2023',
        'fields': {
            'title': 'Paper Title',
            'author': 'Author Name',
            'year': '2023',
            ...
        }
    }
    """
    # Check if it's a Paper or PaperCollection from scholar module
    obj_type = type(obj).__name__
    if obj_type == "PaperCollection":
        # Get BibTeX content from PaperCollection
        include_enriched = kwargs.get("include_enriched", True)
        content = obj._to_bibtex(include_enriched=include_enriched)
        # Write directly so scitex.io.save features work
        encoding = kwargs.get("encoding", "utf-8")
        add_header = kwargs.get("add_header", True)

        with open(spath, "w", encoding=encoding) as f:
            if add_header:
                f.write(f"% BibTeX bibliography\n")
                f.write(
                    f"% Generated by SciTeX on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
                )
                f.write(f"% Number of entries: {len(obj._papers)}\n\n")
            f.write(content)
        return

    elif obj_type == "Paper":
        # Get BibTeX content from Paper
        include_enriched = kwargs.get("include_enriched", True)
        content = obj._to_bibtex(include_enriched=include_enriched)
        # Write directly so scitex.io.save features work
        encoding = kwargs.get("encoding", "utf-8")
        add_header = kwargs.get("add_header", True)

        with open(spath, "w", encoding=encoding) as f:
            if add_header:
                f.write(f"% BibTeX entry\n")
                f.write(
                    f"% Generated by SciTeX on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
                )
            f.write(content)
        return

    # Handle standard dict format
    encoding = kwargs.get("encoding", "utf-8")
    add_header = kwargs.get("add_header", True)
    append = kwargs.get("append", False)

    # Ensure obj is a list
    if isinstance(obj, dict):
        entries = [obj]
    else:
        entries = obj

    # Generate BibTeX content
    lines = []

    if add_header and not append:
        lines.extend(
            [
                f"% BibTeX bibliography",
                f"% Generated by SciTeX on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
                f"% Number of entries: {len(entries)}",
                "",
            ]
        )

    # Convert each entry
    for entry in entries:
        bibtex_str = _entry_to_bibtex(entry)
        lines.append(bibtex_str)
        lines.append("")  # Empty line between entries

    content = "\n".join(lines)

    # Write file
    mode = "a" if append else "w"
    with open(spath, mode, encoding=encoding) as f:
        f.write(content)


def _entry_to_bibtex(entry: Dict[str, Any]) -> str:
    """
    Convert a single entry dict to BibTeX format.

    Args:
        entry: Dictionary with 'entry_type', 'key', and 'fields'

    Returns:
        BibTeX formatted string
    """
    # Extract components
    entry_type = entry.get("entry_type", "misc")
    key = entry.get("key", "unknown")
    fields = entry.get("fields", {})

    # Start building the entry
    lines = [f"@{entry_type}{{{key},"]

    # Add fields
    for field_name, field_value in fields.items():
        # Escape special characters
        escaped_value = _escape_bibtex(str(field_value))

        # Determine if we need braces or quotes
        if "\n" in escaped_value or "{" in escaped_value or "}" in escaped_value:
            # Use braces for multi-line or complex values
            lines.append(f"  {field_name} = {{{escaped_value}}},")
        else:
            # Use braces (standard)
            lines.append(f"  {field_name} = {{{escaped_value}}},")

    # Remove trailing comma from last field
    if lines[-1].endswith(","):
        lines[-1] = lines[-1][:-1]

    lines.append("}")

    return "\n".join(lines)


def _escape_bibtex(text: str) -> str:
    """
    Escape special characters for BibTeX.

    Args:
        text: Text to escape

    Returns:
        Escaped text safe for BibTeX
    """
    # Special characters that need escaping
    replacements = {
        "&": r"\&",
        "%": r"\%",
        "$": r"\$",
        "#": r"\#",
        "_": r"\_",
        # Don't escape braces - they're used for formatting
        # '{': r'\{',
        # '}': r'\}',
    }

    for char, escaped in replacements.items():
        text = text.replace(char, escaped)

    return text


# EOF
