"""Output format plugins for MiniBook.

This module provides a plugin system for generating output in different formats.
Each plugin implements the OutputPlugin interface to provide consistent output generation.
"""

import json
import secrets
from abc import ABC, abstractmethod
from pathlib import Path

from minibook.main import get_git_repo_url
from minibook.utils import get_timestamp, load_template

try:
    from fpdf import FPDF
except ImportError:
    FPDF = None

try:
    from ebooklib import epub
except ImportError:
    epub = None


class OutputPlugin(ABC):
    """Base class for output format plugins.

    All output plugins must implement the generate() method to produce
    output in their specific format.
    """

    name: str = "base"
    extension: str = ".txt"
    description: str = "Base output plugin"

    @abstractmethod
    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "output",
        **kwargs,
    ) -> str:
        """Generate output in the plugin's format.

        Args:
            title: The title of the document
            links: List of (name, url) tuples
            subtitle: Optional subtitle/description
            output_file: Path to the output file
            **kwargs: Additional format-specific options

        Returns:
            str: Path to the generated output file
        """


class HTMLPlugin(OutputPlugin):
    """HTML output plugin using Jinja2 templates."""

    name = "html"
    extension = ".html"
    description = "Generate HTML output with Tailwind CSS styling"

    def __init__(self, template_path: str | None = None):
        """Initialize the HTML plugin.

        Args:
            template_path: Optional path to a custom Jinja2 template
        """
        self.template_path = template_path

    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "index.html",
        **kwargs,
    ) -> str:
        """Generate HTML output.

        Args:
            title: The title of the webpage
            links: List of (name, url) tuples
            subtitle: Optional description
            output_file: Path to the output HTML file
            **kwargs: Additional options (nonce for CSP)

        Returns:
            str: Path to the generated HTML file
        """
        template = load_template(self.template_path)
        timestamp = get_timestamp()
        nonce = kwargs.get("nonce") or secrets.token_urlsafe(16)

        html = template.render(
            title=title,
            links=links,
            description=subtitle,
            timestamp=timestamp,
            repository_url=get_git_repo_url(),
            nonce=nonce,
        )

        output_path = Path(output_file)
        with output_path.open("w") as f:
            f.write(html)

        return str(output_path)


class MarkdownPlugin(OutputPlugin):
    """Markdown output plugin."""

    name = "markdown"
    extension = ".md"
    description = "Generate Markdown output"

    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "links.md",
        **kwargs,
    ) -> str:
        """Generate Markdown output.

        Args:
            title: The title of the document
            links: List of (name, url) tuples
            subtitle: Optional description
            output_file: Path to the output Markdown file
            **kwargs: Additional options

        Returns:
            str: Path to the generated Markdown file
        """
        lines = [f"# {title}", ""]

        if subtitle:
            lines.extend([f"*{subtitle}*", ""])

        lines.append("## Links")
        lines.append("")

        for name, url in links:
            lines.append(f"- [{name}]({url})")

        lines.append("")
        lines.append("---")
        lines.append("")
        timestamp = get_timestamp()
        lines.append(f"*Generated by [MiniBook](https://pypi.org/project/minibook/) on {timestamp}*")
        lines.append("")

        content = "\n".join(lines)

        output_path = Path(output_file)
        with output_path.open("w") as f:
            f.write(content)

        return str(output_path)


class JSONPlugin(OutputPlugin):
    """JSON output plugin."""

    name = "json"
    extension = ".json"
    description = "Generate JSON output"

    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "links.json",
        **kwargs,
    ) -> str:
        """Generate JSON output.

        Args:
            title: The title of the document
            links: List of (name, url) tuples
            subtitle: Optional description
            output_file: Path to the output JSON file
            **kwargs: Additional options

        Returns:
            str: Path to the generated JSON file
        """
        timestamp = get_timestamp()

        data = {
            "title": title,
            "description": subtitle,
            "links": [{"name": name, "url": url} for name, url in links],
            "metadata": {
                "generated_by": "MiniBook",
                "timestamp": timestamp,
                "repository_url": get_git_repo_url(),
            },
        }

        content = json.dumps(data, indent=2)

        output_path = Path(output_file)
        with output_path.open("w") as f:
            f.write(content)

        return str(output_path)


class PDFPlugin(OutputPlugin):
    """PDF output plugin using fpdf2."""

    name = "pdf"
    extension = ".pdf"
    description = "Generate PDF output"

    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "links.pdf",
        **kwargs,
    ) -> str:
        """Generate PDF output.

        Args:
            title: The title of the document
            links: List of (name, url) tuples
            subtitle: Optional description
            output_file: Path to the output PDF file
            **kwargs: Additional options

        Returns:
            str: Path to the generated PDF file

        Raises:
            ImportError: If fpdf2 is not installed
        """
        if FPDF is None:
            raise ImportError("PDF generation requires fpdf2. Install with: uv add fpdf2")  # noqa: TRY003

        timestamp = get_timestamp()

        pdf = FPDF()
        pdf.add_page()
        pdf.set_auto_page_break(auto=True, margin=15)

        # Title
        pdf.set_font("Helvetica", "B", 24)
        pdf.cell(0, 15, title, new_x="LMARGIN", new_y="NEXT", align="C")

        # Subtitle
        if subtitle:
            pdf.set_font("Helvetica", "I", 12)
            pdf.set_text_color(100, 100, 100)
            pdf.cell(0, 10, subtitle, new_x="LMARGIN", new_y="NEXT", align="C")
            pdf.set_text_color(0, 0, 0)

        pdf.ln(10)

        # Links section header
        pdf.set_font("Helvetica", "B", 16)
        pdf.cell(0, 10, "Links", new_x="LMARGIN", new_y="NEXT")
        pdf.ln(5)

        # Links
        pdf.set_font("Helvetica", "", 11)
        for name, url in links:
            # Link name in bold
            pdf.set_font("Helvetica", "B", 11)
            pdf.cell(0, 8, f"- {name}", new_x="LMARGIN", new_y="NEXT")
            # URL in blue, smaller
            pdf.set_font("Helvetica", "", 9)
            pdf.set_text_color(0, 0, 200)
            pdf.cell(0, 6, f"  {url}", new_x="LMARGIN", new_y="NEXT", link=url)
            pdf.set_text_color(0, 0, 0)
            pdf.ln(2)

        # Footer
        pdf.ln(15)
        pdf.set_font("Helvetica", "I", 9)
        pdf.set_text_color(128, 128, 128)
        pdf.cell(0, 8, f"Generated by MiniBook on {timestamp}", new_x="LMARGIN", new_y="NEXT", align="C")
        pdf.cell(0, 6, "https://pypi.org/project/minibook/", new_x="LMARGIN", new_y="NEXT", align="C")

        output_path = Path(output_file)
        pdf.output(str(output_path))

        return str(output_path)


class RSTPlugin(OutputPlugin):
    """reStructuredText output plugin."""

    name = "rst"
    extension = ".rst"
    description = "Generate reStructuredText output"

    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "links.rst",
        **kwargs,
    ) -> str:
        """Generate reStructuredText output.

        Args:
            title: The title of the document
            links: List of (name, url) tuples
            subtitle: Optional description
            output_file: Path to the output RST file
            **kwargs: Additional options

        Returns:
            str: Path to the generated RST file
        """
        timestamp = get_timestamp()

        lines = []

        # Title with RST underline (must be at least as long as title)
        title_underline = "=" * len(title)
        lines.extend([title_underline, title, title_underline, ""])

        if subtitle:
            lines.extend([f"*{subtitle}*", ""])

        # Links section
        lines.extend(["Links", "-----", ""])

        for name, url in links:
            # RST link format: `Link Text <URL>`_
            lines.append(f"* `{name} <{url}>`_")

        lines.extend(["", "----", ""])

        # Footer
        lines.append(f"*Generated by* `MiniBook <https://pypi.org/project/minibook/>`_ *on {timestamp}*")
        lines.append("")

        content = "\n".join(lines)

        output_path = Path(output_file)
        with output_path.open("w") as f:
            f.write(content)

        return str(output_path)


class EPUBPlugin(OutputPlugin):
    """EPUB output plugin using ebooklib."""

    name = "epub"
    extension = ".epub"
    description = "Generate EPUB ebook output"

    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "links.epub",
        **kwargs,
    ) -> str:
        """Generate EPUB output.

        Args:
            title: The title of the ebook
            links: List of (name, url) tuples
            subtitle: Optional description
            output_file: Path to the output EPUB file
            **kwargs: Additional options (author, language)

        Returns:
            str: Path to the generated EPUB file

        Raises:
            ImportError: If ebooklib is not installed
        """
        if epub is None:
            raise ImportError("EPUB generation requires ebooklib. Install with: pip install minibook[epub]")  # noqa: TRY003

        timestamp = get_timestamp()
        author = kwargs.get("author", "MiniBook")
        language = kwargs.get("language", "en")

        # Create EPUB book
        book = epub.EpubBook()

        # Set metadata
        book.set_identifier(f"minibook-{timestamp}")
        book.set_title(title)
        book.set_language(language)
        book.add_author(author)

        # Create content chapter
        chapter = epub.EpubHtml(title="Links", file_name="links.xhtml", lang=language)

        # Build HTML content for the chapter
        # Note: ebooklib requires proper XHTML content
        html_parts = [
            f"<h1>{title}</h1>",
        ]

        if subtitle:
            html_parts.append(f'<p class="subtitle">{subtitle}</p>')

        html_parts.append("<h2>Links</h2>")
        html_parts.append("<ul>")

        for name, url in links:
            html_parts.append(f'<li><a href="{url}">{name}</a></li>')

        html_parts.append("</ul>")
        html_parts.append(f'<p class="footer">Generated by MiniBook on {timestamp}</p>')

        chapter.content = "\n".join(html_parts)

        # Add default CSS
        style = epub.EpubItem(
            uid="style",
            file_name="style.css",
            media_type="text/css",
            content=b"""
body { font-family: Georgia, serif; margin: 2em; }
h1 { color: #333; }
.subtitle { color: #666; font-style: italic; margin-bottom: 1.5em; }
ul { list-style-type: disc; }
li { margin: 0.5em 0; }
a { color: #0066cc; text-decoration: none; }
.footer { margin-top: 2em; font-size: 0.9em; color: #888; }
""",
        )
        book.add_item(style)

        # Add chapter to book
        book.add_item(chapter)

        # Add navigation
        book.toc = [epub.Link("links.xhtml", "Links", "links")]
        book.add_item(epub.EpubNcx())
        book.add_item(epub.EpubNav())

        # Set spine
        book.spine = ["nav", chapter]

        output_path = Path(output_file)
        epub.write_epub(str(output_path), book)

        return str(output_path)


class AsciiDocPlugin(OutputPlugin):
    """AsciiDoc output plugin."""

    name = "asciidoc"
    extension = ".adoc"
    description = "Generate AsciiDoc output"

    def generate(
        self,
        title: str,
        links: list[tuple[str, str]],
        subtitle: str | None = None,
        output_file: str | Path = "links.adoc",
        **kwargs,
    ) -> str:
        """Generate AsciiDoc output.

        Args:
            title: The title of the document
            links: List of (name, url) tuples
            subtitle: Optional description
            output_file: Path to the output AsciiDoc file
            **kwargs: Additional options

        Returns:
            str: Path to the generated AsciiDoc file
        """
        timestamp = get_timestamp()

        lines = []

        # AsciiDoc document header
        lines.append(f"= {title}")
        lines.append(":toc:")
        lines.append(":icons: font")
        lines.append("")

        if subtitle:
            lines.append(f"_{subtitle}_")
            lines.append("")

        # Links section
        lines.append("== Links")
        lines.append("")

        for name, url in links:
            # AsciiDoc link format: link:URL[Text]
            lines.append(f"* link:{url}[{name}]")

        lines.append("")
        lines.append("'''")
        lines.append("")

        # Footer
        lines.append(f"_Generated by link:https://pypi.org/project/minibook/[MiniBook] on {timestamp}_")
        lines.append("")

        content = "\n".join(lines)

        output_path = Path(output_file)
        with output_path.open("w") as f:
            f.write(content)

        return str(output_path)


# Registry of available plugins
PLUGINS: dict[str, type[OutputPlugin]] = {
    "html": HTMLPlugin,
    "markdown": MarkdownPlugin,
    "md": MarkdownPlugin,  # Alias
    "json": JSONPlugin,
    "pdf": PDFPlugin,
    "rst": RSTPlugin,
    "restructuredtext": RSTPlugin,  # Alias
    "epub": EPUBPlugin,
    "asciidoc": AsciiDocPlugin,
    "adoc": AsciiDocPlugin,  # Alias
}


def get_plugin(name: str) -> type[OutputPlugin]:
    """Get an output plugin by name.

    Args:
        name: The name of the plugin (e.g., "html", "markdown", "json")

    Returns:
        The plugin class

    Raises:
        ValueError: If the plugin name is not recognized
    """
    name_lower = name.lower()
    if name_lower not in PLUGINS:
        available = ", ".join(sorted(set(PLUGINS.keys())))
        msg = f"Unknown output format '{name}'. Available formats: {available}"
        raise ValueError(msg)
    return PLUGINS[name_lower]


def list_plugins() -> list[dict[str, str]]:
    """List all available output plugins.

    Returns:
        List of dicts with plugin info (name, extension, description)
    """
    seen = set()
    result = []
    for _name, plugin_cls in PLUGINS.items():
        if plugin_cls.name not in seen:
            seen.add(plugin_cls.name)
            result.append(
                {
                    "name": plugin_cls.name,
                    "extension": plugin_cls.extension,
                    "description": plugin_cls.description,
                }
            )
    return result
