"""
HTML-to-PDF conversion for MicroPDF.

This module provides HTML-to-PDF conversion with a builder-style options API
for controlling page size, margins, headers/footers, and more.

Example usage:
    from micropdf.html_to_pdf import HtmlOptions, html_to_pdf, html_file_to_pdf

    # Simple conversion
    html_to_pdf("<h1>Hello World</h1>", "output.pdf")

    # With options
    opts = HtmlOptions()
    opts.set_page_size(HtmlOptions.A4)
    opts.set_margins(top=20.0, right=15.0, bottom=20.0, left=15.0)
    opts.set_landscape(True)
    html_to_pdf("<h1>Hello</h1>", "output.pdf", opts)
    opts.drop()
"""

from enum import IntEnum
from pathlib import Path
from typing import Optional, Union

from .ffi_native import lib, is_native_available
from .errors import MicroPDFError, ErrorCode


class PageSize(IntEnum):
    """Standard page sizes."""
    A3 = 0
    A4 = 1
    A5 = 2
    LETTER = 3
    LEGAL = 4
    TABLOID = 5


def _check_native() -> None:
    if not is_native_available():
        raise MicroPDFError(
            ErrorCode.SYSTEM,
            "Native library not available."
        )


def _check_result(result: int, operation: str) -> None:
    if result < 0:
        raise MicroPDFError(ErrorCode.GENERIC, f"{operation}: error code {result}")


def _to_path_bytes(path: Union[str, Path]) -> bytes:
    return str(path).encode('utf-8')


class HtmlOptions:
    """Builder for HTML-to-PDF conversion options."""

    A3 = PageSize.A3
    A4 = PageSize.A4
    A5 = PageSize.A5
    LETTER = PageSize.LETTER
    LEGAL = PageSize.LEGAL
    TABLOID = PageSize.TABLOID

    __slots__ = ('_handle',)

    def __init__(self) -> None:
        _check_native()
        self._handle = lib.mp_html_options_create()
        if self._handle == 0:
            raise MicroPDFError(ErrorCode.GENERIC, "Failed to create HTML options")

    def set_page_size(self, size: PageSize) -> 'HtmlOptions':
        """Set a standard page size."""
        _check_result(
            lib.mp_html_options_set_page_size(self._handle, int(size)),
            "set_page_size"
        )
        return self

    def set_page_size_custom(self, width: float, height: float) -> 'HtmlOptions':
        """Set a custom page size in points."""
        _check_result(
            lib.mp_html_options_set_page_size_custom(self._handle, width, height),
            "set_page_size_custom"
        )
        return self

    def set_margins(
        self,
        top: float = 0.0,
        right: float = 0.0,
        bottom: float = 0.0,
        left: float = 0.0,
    ) -> 'HtmlOptions':
        """Set page margins in points."""
        _check_result(
            lib.mp_html_options_set_margins(self._handle, top, right, bottom, left),
            "set_margins"
        )
        return self

    def set_scale(self, scale: float) -> 'HtmlOptions':
        """Set the rendering scale factor."""
        _check_result(
            lib.mp_html_options_set_scale(self._handle, scale),
            "set_scale"
        )
        return self

    def set_javascript(self, enabled: bool) -> 'HtmlOptions':
        """Enable or disable JavaScript execution."""
        _check_result(
            lib.mp_html_options_set_javascript(self._handle, int(enabled)),
            "set_javascript"
        )
        return self

    def set_print_background(self, enabled: bool) -> 'HtmlOptions':
        """Enable or disable printing background graphics."""
        _check_result(
            lib.mp_html_options_set_print_background(self._handle, int(enabled)),
            "set_print_background"
        )
        return self

    def set_landscape(self, landscape: bool) -> 'HtmlOptions':
        """Set landscape or portrait orientation."""
        _check_result(
            lib.mp_html_options_set_landscape(self._handle, int(landscape)),
            "set_landscape"
        )
        return self

    def set_stylesheet(self, css: str) -> 'HtmlOptions':
        """Set additional CSS stylesheet content."""
        _check_result(
            lib.mp_html_options_set_stylesheet(self._handle, css.encode('utf-8')),
            "set_stylesheet"
        )
        return self

    def set_base_url(self, url: str) -> 'HtmlOptions':
        """Set the base URL for resolving relative paths."""
        _check_result(
            lib.mp_html_options_set_base_url(self._handle, url.encode('utf-8')),
            "set_base_url"
        )
        return self

    def set_header(self, html: str) -> 'HtmlOptions':
        """Set HTML content for the page header."""
        _check_result(
            lib.mp_html_options_set_header(self._handle, html.encode('utf-8')),
            "set_header"
        )
        return self

    def set_footer(self, html: str) -> 'HtmlOptions':
        """Set HTML content for the page footer."""
        _check_result(
            lib.mp_html_options_set_footer(self._handle, html.encode('utf-8')),
            "set_footer"
        )
        return self

    @property
    def page_width(self) -> float:
        """Get the current page width in points."""
        return lib.mp_html_options_get_page_width(self._handle)

    @property
    def page_height(self) -> float:
        """Get the current page height in points."""
        return lib.mp_html_options_get_page_height(self._handle)

    @property
    def content_width(self) -> float:
        """Get the content area width (page width minus margins)."""
        return lib.mp_html_options_get_content_width(self._handle)

    @property
    def content_height(self) -> float:
        """Get the content area height (page height minus margins)."""
        return lib.mp_html_options_get_content_height(self._handle)

    def drop(self) -> None:
        """Release the options resources."""
        if getattr(self, '_handle', 0) != 0:
            lib.mp_html_options_free(self._handle)
            self._handle = 0

    def __del__(self) -> None:
        try:
            self.drop()
        except Exception:
            pass

    def __enter__(self) -> 'HtmlOptions':
        return self

    def __exit__(self, *args: object) -> None:
        self.drop()


def html_to_pdf(
    html: str,
    output_path: Union[str, Path],
    options: Optional[HtmlOptions] = None,
) -> None:
    """
    Convert an HTML string to a PDF file.

    Args:
        html: HTML content string.
        output_path: Path for the output PDF.
        options: Optional conversion options.

    Raises:
        MicroPDFError: If conversion fails.
    """
    _check_native()
    opts_handle = options._handle if options else 0
    result = lib.mp_html_to_pdf(
        html.encode('utf-8'),
        _to_path_bytes(output_path),
        opts_handle,
    )
    _check_result(result, "html_to_pdf")


def html_file_to_pdf(
    html_path: Union[str, Path],
    output_path: Union[str, Path],
    options: Optional[HtmlOptions] = None,
) -> None:
    """
    Convert an HTML file to a PDF file.

    Args:
        html_path: Path to the input HTML file.
        output_path: Path for the output PDF.
        options: Optional conversion options.

    Raises:
        MicroPDFError: If conversion fails.
    """
    _check_native()
    opts_handle = options._handle if options else 0
    result = lib.mp_html_file_to_pdf(
        _to_path_bytes(html_path),
        _to_path_bytes(output_path),
        opts_handle,
    )
    _check_result(result, "html_file_to_pdf")


__all__ = [
    "PageSize",
    "HtmlOptions",
    "html_to_pdf",
    "html_file_to_pdf",
]
