"""
Consolidated FFI and native library loading for MicroPDF Python bindings.

This module handles:
1. Platform detection
2. Native library discovery and loading
3. FFI bindings via cffi
4. Fallback mock library for testing/development

Usage:
    from micropdf.ffi_native import ffi, lib, FZ_STORE_DEFAULT, get_library_path
"""

import os
import sys
import platform
import urllib.request
import tarfile
import tempfile
import shutil
from pathlib import Path
from typing import Optional
from cffi import FFI

# =============================================================================
# Version
# =============================================================================

try:
    from .version import __version__
except ImportError:
    __version__ = "0.6.0"

# =============================================================================
# Platform Detection
# =============================================================================

PLATFORM_MAP = {
    "Linux": "linux",
    "Darwin": "darwin",
    "Windows": "win32",
}

ARCH_MAP = {
    "x86_64": "x64",
    "AMD64": "x64",
    "aarch64": "arm64",
    "arm64": "arm64",
    "i386": "ia32",
    "i686": "ia32",
}

LIB_NAMES = {
    "linux": "libmicropdf.so",
    "darwin": "libmicropdf.dylib",
    "win32": "micropdf.dll",
}


def get_platform_info() -> tuple[str, str]:
    """Get the current platform and architecture."""
    system = platform.system()
    machine = platform.machine()
    plat = PLATFORM_MAP.get(system, system.lower())
    arch = ARCH_MAP.get(machine, machine)
    return plat, arch


def get_lib_name() -> str:
    """Get the library name for the current platform."""
    plat, _ = get_platform_info()
    return LIB_NAMES.get(plat, "libmicropdf.so")


# =============================================================================
# Library Discovery
# =============================================================================

def find_library() -> Optional[Path]:
    """
    Find the native library.

    Search order:
    1. MICROPDF_LIB_PATH environment variable
    2. Bundled in package (lib/<platform>-<arch>/)
    3. Rust build output (../micropdf-rs/target/release/)
    4. System library paths

    Returns:
        Path to the library, or None if not found.
    """
    plat, arch = get_platform_info()
    lib_name = get_lib_name()
    package_dir = Path(__file__).parent

    # 1. Check environment variable first
    env_path = os.environ.get("MICROPDF_LIB_PATH")
    if env_path:
        env_lib = Path(env_path)
        if env_lib.is_file() and env_lib.exists():
            return env_lib
        if env_lib.is_dir():
            for candidate in [lib_name, "libmicropdf.so", "libmicropdf.dylib", "micropdf.dll"]:
                candidate_path = env_lib / candidate
                if candidate_path.exists():
                    return candidate_path

    # 2. Check bundled library
    lib_dir = package_dir / "lib" / f"{plat}-{arch}"
    bundled_path = lib_dir / lib_name
    if bundled_path.exists():
        return bundled_path

    # Also check lib directory for any matching library
    if lib_dir.exists():
        for f in lib_dir.iterdir():
            if f.suffix in (".so", ".dylib", ".dll") and "micropdf" in f.name:
                return f

    # 3. Check Rust build output (development mode)
    rust_target = package_dir.parent.parent.parent / "micropdf-rs" / "target" / "release" / lib_name
    if rust_target.exists():
        return rust_target

    # Also check debug build
    rust_debug = package_dir.parent.parent.parent / "micropdf-rs" / "target" / "debug" / lib_name
    if rust_debug.exists():
        return rust_debug

    # 4. Check system library paths
    system_paths = []
    if plat == "linux":
        system_paths = ["/usr/local/lib", "/usr/lib", "/lib", os.path.expanduser("~/.local/lib")]
    elif plat == "darwin":
        system_paths = ["/usr/local/lib", "/opt/homebrew/lib"]

    for system_path in system_paths:
        system_lib = Path(system_path) / lib_name
        if system_lib.exists():
            return system_lib

    return None


def download_library() -> Optional[Path]:
    """
    Download the prebuilt library.

    Returns:
        Path to the downloaded library, or None if download failed.
    """
    if os.environ.get("MICROPDF_SKIP_DOWNLOAD", "").lower() in ("1", "true", "yes"):
        return None

    plat, arch = get_platform_info()
    lib_name = get_lib_name()
    download_url = f"https://bitbucket.org/lexmata/micropdf/downloads/micropdf-{plat}-{arch}.tar.gz"

    package_dir = Path(__file__).parent
    lib_dir = package_dir / "lib" / f"{plat}-{arch}"
    target_path = lib_dir / lib_name

    try:
        lib_dir.mkdir(parents=True, exist_ok=True)

        with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete=False) as tmp:
            tmp_path = Path(tmp.name)

        urllib.request.urlretrieve(download_url, tmp_path)

        with tarfile.open(tmp_path, "r:gz") as tar:
            tar.extractall(lib_dir.parent)

        tmp_path.unlink()

        if target_path.exists():
            return target_path

        # Check if extraction put files in a subdirectory
        for pattern in ["*.so", "*.dylib", "*.dll"]:
            for f in lib_dir.parent.rglob(pattern):
                if "micropdf" in f.name:
                    shutil.copy(f, target_path)
                    return target_path

        return None

    except Exception:
        return None


def load_library() -> Optional[Path]:
    """
    Load the native MicroPDF library.

    Returns:
        Path to the library, or None if not available.
    """
    lib_path = find_library()
    if lib_path:
        return lib_path

    lib_path = download_library()
    if lib_path:
        return lib_path

    return None


# Module-level library path cache
_library_path: Optional[Path] = None


def get_library_path() -> Optional[Path]:
    """Get the path to the native library (cached)."""
    global _library_path
    if _library_path is None:
        _library_path = load_library()
    return _library_path


# =============================================================================
# FFI Definitions
# =============================================================================

ffi = FFI()

# Core FFI definitions
ffi.cdef("""
    // Type aliases
    typedef int8_t i8;
    typedef int16_t i16;
    typedef int32_t i32;
    typedef int64_t i64;
    typedef uint8_t u8;
    typedef uint16_t u16;
    typedef uint32_t u32;
    typedef uint64_t u64;
    typedef float f32;
    typedef double f64;

    // Geometry structures
    typedef struct { float x, y; } fz_point;
    typedef struct { float x0, y0, x1, y1; } fz_rect;
    typedef struct { int x0, y0, x1, y1; } fz_irect;
    typedef struct { float a, b, c, d, e, f; } fz_matrix;
    typedef struct { fz_point ul, ur, ll, lr; } fz_quad;

    // Opaque handle types
    typedef uint64_t fz_context;
    typedef uint64_t fz_document;
    typedef uint64_t fz_page;
    typedef uint64_t fz_pixmap;
    typedef uint64_t fz_buffer;
    typedef uint64_t fz_colorspace;
    typedef uint64_t fz_stext_page;
    typedef uint64_t fz_link;
    typedef uint64_t fz_stream;
    typedef uint64_t fz_output;
    typedef uint64_t fz_font;
    typedef uint64_t fz_image;
    typedef uint64_t fz_cookie;
    typedef uint64_t fz_device_handle;
    typedef uint64_t fz_path;

    // Context functions
    fz_context fz_new_context(const void* alloc, const void* locks, size_t max_store);
    void fz_drop_context(fz_context ctx);
    fz_context fz_clone_context(fz_context ctx);

    // Document functions
    fz_document fz_open_document(fz_context ctx, const char* filename);
    fz_document fz_open_document_with_buffer(fz_context ctx, const char* magic, const unsigned char* data, size_t len);
    void fz_drop_document(fz_context ctx, fz_document doc);
    int fz_count_pages(fz_context ctx, fz_document doc);
    int fz_needs_password(fz_context ctx, fz_document doc);
    int fz_authenticate_password(fz_context ctx, fz_document doc, const char* password);
    int fz_has_permission(fz_context ctx, fz_document doc, int permission);
    int fz_lookup_metadata(fz_context ctx, fz_document doc, const char* key, char* buf, int size);
    void pdf_save_document(fz_context ctx, fz_document doc, const char* filename, const void* opts);

    // Page functions
    fz_page fz_load_page(fz_context ctx, fz_document doc, int number);
    void fz_drop_page(fz_context ctx, fz_page page);
    fz_rect fz_bound_page(fz_context ctx, fz_page page);

    // Colorspace functions
    fz_colorspace fz_device_rgb(fz_context ctx);
    fz_colorspace fz_device_gray(fz_context ctx);
    fz_colorspace fz_device_bgr(fz_context ctx);
    fz_colorspace fz_device_cmyk(fz_context ctx);
    int fz_colorspace_n(fz_context ctx, fz_colorspace cs);
    const char* fz_colorspace_name(fz_context ctx, fz_colorspace cs);

    // Matrix functions
    fz_matrix fz_identity(void);
    fz_matrix fz_scale(float sx, float sy);
    fz_matrix fz_translate(float tx, float ty);
    fz_matrix fz_rotate(float degrees);
    fz_matrix fz_concat(fz_matrix a, fz_matrix b);

    // Pixmap functions
    fz_pixmap fz_new_pixmap(fz_context ctx, fz_colorspace cs, int w, int h, int alpha);
    fz_pixmap fz_new_pixmap_from_page(fz_context ctx, fz_page page, fz_matrix ctm, fz_colorspace cs, int alpha);
    void fz_drop_pixmap(fz_context ctx, fz_pixmap pix);
    int fz_pixmap_width(fz_context ctx, fz_pixmap pix);
    int fz_pixmap_height(fz_context ctx, fz_pixmap pix);
    int fz_pixmap_components(fz_context ctx, fz_pixmap pix);
    int fz_pixmap_stride(fz_context ctx, fz_pixmap pix);
    unsigned char* fz_pixmap_samples(fz_context ctx, fz_pixmap pix);
    void fz_clear_pixmap(fz_context ctx, fz_pixmap pix);

    // Buffer functions
    fz_buffer fz_new_buffer(fz_context ctx, size_t capacity);
    fz_buffer fz_new_buffer_from_copied_data(fz_context ctx, const unsigned char* data, size_t size);
    void fz_drop_buffer(fz_context ctx, fz_buffer buf);
    size_t fz_buffer_storage(fz_context ctx, fz_buffer buf, unsigned char** datap);
    const unsigned char* fz_buffer_data(fz_context ctx, fz_buffer buf, size_t* len);
    void fz_append_data(fz_context ctx, fz_buffer buf, const void* data, size_t len);
    void fz_append_string(fz_context ctx, fz_buffer buf, const char* str);
    void fz_clear_buffer(fz_context ctx, fz_buffer buf);
    fz_buffer fz_new_buffer_from_pixmap_as_png(fz_context ctx, fz_pixmap pix, int color_params);

    // Text extraction functions
    fz_stext_page fz_new_stext_page_from_page(fz_context ctx, fz_page page, const void* options);
    void fz_drop_stext_page(fz_context ctx, fz_stext_page stext);
    fz_buffer fz_new_buffer_from_stext_page(fz_context ctx, fz_stext_page stext);
    int fz_search_stext_page(fz_context ctx, fz_stext_page stext, const char* needle, int* mark, fz_quad* hit_bbox, int hit_max);

    // Link functions
    fz_link fz_load_links(fz_context ctx, fz_page page);
    void fz_drop_link(fz_context ctx, fz_link link);
    fz_rect fz_link_rect(fz_context ctx, fz_link link);
    const char* fz_link_uri(fz_context ctx, fz_link link);
    fz_link fz_link_next(fz_context ctx, fz_link link);

    // Stream functions
    fz_stream fz_open_file(fz_context ctx, const char* filename);
    fz_stream fz_open_memory(fz_context ctx, const unsigned char* data, size_t len);
    void fz_drop_stream(fz_context ctx, fz_stream stm);
    size_t fz_read(fz_context ctx, fz_stream stm, unsigned char* data, size_t len);
    int fz_read_byte(fz_context ctx, fz_stream stm);
    int fz_is_eof(fz_context ctx, fz_stream stm);
    void fz_seek(fz_context ctx, fz_stream stm, int64_t offset, int whence);
    int64_t fz_tell(fz_context ctx, fz_stream stm);

    // Output functions
    fz_output fz_new_output_with_path(fz_context ctx, const char* filename, int append);
    fz_output fz_new_output_with_buffer(fz_context ctx, fz_buffer buf);
    void fz_drop_output(fz_context ctx, fz_output out);
    void fz_write_data(fz_context ctx, fz_output out, const void* data, size_t size);
    void fz_write_string(fz_context ctx, fz_output out, const char* s);
    void fz_write_byte(fz_context ctx, fz_output out, unsigned char byte);
    int64_t fz_tell_output(fz_context ctx, fz_output out);

    // Font functions
    fz_font fz_new_font(fz_context ctx, const char* name, int is_bold, int is_italic, uint64_t font_file);
    fz_font fz_new_font_from_file(fz_context ctx, const char* name, const char* path, int index, int use_glyph_bbox);
    fz_font fz_new_font_from_memory(fz_context ctx, const char* name, const unsigned char* data, int len, int index, int use_glyph_bbox);
    void fz_drop_font(fz_context ctx, fz_font font);
    void fz_font_name(fz_context ctx, fz_font font, char* buf, int size);
    int fz_font_is_bold(fz_context ctx, fz_font font);
    int fz_font_is_italic(fz_context ctx, fz_font font);

    // Image functions
    fz_image fz_new_image_from_file(fz_context ctx, const char* path);
    fz_image fz_new_image_from_buffer(fz_context ctx, fz_buffer buffer);
    fz_image fz_new_image_from_pixmap(fz_context ctx, fz_pixmap pixmap, fz_image mask);
    void fz_drop_image(fz_context ctx, fz_image image);
    int fz_image_width(fz_context ctx, fz_image image);
    int fz_image_height(fz_context ctx, fz_image image);

    // Cookie functions
    fz_cookie fz_new_cookie(fz_context ctx);
    void fz_drop_cookie(fz_context ctx, fz_cookie cookie);
    void fz_abort_cookie(fz_context ctx, fz_cookie cookie);
    int fz_cookie_is_aborted(fz_context ctx, fz_cookie cookie);

    // Path functions
    fz_path fz_new_path(fz_context ctx);
    void fz_drop_path(fz_context ctx, fz_path path);
    void fz_moveto(fz_context ctx, fz_path path, float x, float y);
    void fz_lineto(fz_context ctx, fz_path path, float x, float y);
    void fz_curveto(fz_context ctx, fz_path path, float x1, float y1, float x2, float y2, float x3, float y3);
    void fz_closepath(fz_context ctx, fz_path path);
    void fz_rectto(fz_context ctx, fz_path path, float x, float y, float w, float h);

    // Enhanced MicroPDF Functions
    char* mp_get_last_enhanced_error(void);
    int mp_write_pdf(uint64_t ctx, uint64_t doc, const char *path);
    int mp_add_blank_page(uint64_t ctx, uint64_t doc, float width, float height);
    int mp_merge_pdfs(uint64_t ctx, const char *const *paths, int count, const char *output_path);
    int mp_split_pdf(uint64_t ctx, const char *input_path, const char *output_dir);
    int mp_reorder_pages(uint64_t ctx, const char *input_path, const char *output_path, const int *page_order, int page_order_count);
    int mp_add_watermark(uint64_t ctx, const char *input_path, const char *output_path, const char *text, float x, float y, float font_size, float opacity);
    int mp_overlay_pdf(uint64_t ctx, const char *base_path, const char *output_path, const char *overlay_path, float opacity);
    int mp_optimize_pdf(uint64_t ctx, const char *path);
    int mp_linearize_pdf(uint64_t ctx, const char *input_path, const char *output_path);
    int mp_draw_line(uint64_t ctx, uint64_t page, float x0, float y0, float x1, float y1, float r, float g, float b, float alpha, float line_width);
    int mp_draw_rectangle(uint64_t ctx, uint64_t page, float x, float y, float width, float height, float r, float g, float b, float alpha, int fill);
    int mp_draw_circle(uint64_t ctx, uint64_t page, float x, float y, float radius, float r, float g, float b, float alpha, int fill);
    int mp_restore_bookmarks(const char *input_path, const char *output_path, const char *bookmarks_json);
    char* mp_get_bookmarks(const char *pdf_path);
    void mp_free_string(char *s);

    // Highlight and text overlay structs
    typedef struct { float width; float height; } PageDim;
    typedef struct { int page; float x, y, width, height; float r, g, b, alpha; } HighlightRect;
    typedef struct { const char *text; float x, y, height; float font_size; const char *font_name; float r, g, b; int render_mode; } TextOverlayElement;
    typedef struct { float x, y, width, height; const unsigned char *data; size_t data_len; int format; } ImageOverlayElement;

    int mp_create_highlight_overlay(const char *output_path, const PageDim *page_dims, int page_count, const HighlightRect *highlights, int highlight_count);
    uint64_t mp_register_font(const char *font_name, const unsigned char *font_data, size_t data_len);
    int mp_create_text_overlay(const char *output_path, float width, float height, const uint64_t *font_handles, int font_count, const TextOverlayElement *texts, int text_count, const ImageOverlayElement *image);
    void mp_font_free(uint64_t handle);

    // ============================================================================
    // Security Functions - Certificates, Signatures, Encryption
    // ============================================================================

    typedef struct {
        int valid;
        int certificate_valid;
        int document_modified;
        int has_timestamp;
        const char *signer_name;
        long long signing_time;
        const char *error_message;
    } SignatureVerifyResult;

    uint64_t mp_certificate_load_pkcs12(const char *path, const char *password);
    uint64_t mp_certificate_load_pem(const char *cert_path, const char *key_path, const char *key_password);
    void mp_certificate_drop(uint64_t cert);
    const char* mp_certificate_get_subject(uint64_t cert);
    const char* mp_certificate_get_issuer(uint64_t cert);
    int mp_certificate_is_valid(uint64_t cert);

    int mp_signature_create(const char *input_path, const char *output_path, uint64_t cert, const char *field_name, int page, float x, float y, float width, float height, const char *reason, const char *location);
    int mp_signature_create_invisible(const char *input_path, const char *output_path, uint64_t cert, const char *field_name, const char *reason, const char *location);
    int mp_signature_verify(const char *pdf_path, const char *field_name, SignatureVerifyResult *result);
    int mp_signature_count(const char *pdf_path);

    int mp_tsa_timestamp(const char *tsa_url, const unsigned char *data, size_t data_len, const unsigned char **timestamp_out, size_t *timestamp_len_out);

    uint64_t mp_encryption_options_new(void);
    int mp_encryption_set_user_password(uint64_t options, const char *password);
    int mp_encryption_set_owner_password(uint64_t options, const char *password);
    int mp_encryption_set_algorithm(uint64_t options, int algorithm);
    int mp_encryption_set_permissions(uint64_t options, int permissions);
    void mp_encryption_options_drop(uint64_t options);
    int mp_encrypt_pdf(const char *input_path, const char *output_path, uint64_t options);
    int mp_decrypt_pdf(const char *input_path, const char *output_path, const char *password);
    int mp_is_encrypted(const char *pdf_path);

    void mp_free_timestamp(unsigned char *data, size_t len);
    void mp_signature_verify_result_free(SignatureVerifyResult *result);

    // ============================================================================
    // HTML-to-PDF Functions
    // ============================================================================

    uint64_t mp_html_options_create(void);
    void mp_html_options_free(uint64_t handle);
    int mp_html_options_set_page_size(uint64_t handle, int page_size);
    int mp_html_options_set_page_size_custom(uint64_t handle, float width, float height);
    int mp_html_options_set_margins(uint64_t handle, float top, float right, float bottom, float left);
    int mp_html_options_set_scale(uint64_t handle, float scale);
    int mp_html_options_set_javascript(uint64_t handle, int enabled);
    int mp_html_options_set_print_background(uint64_t handle, int enabled);
    int mp_html_options_set_landscape(uint64_t handle, int landscape);
    int mp_html_options_set_stylesheet(uint64_t handle, const char *css);
    int mp_html_options_set_base_url(uint64_t handle, const char *url);
    int mp_html_options_set_header(uint64_t handle, const char *html);
    int mp_html_options_set_footer(uint64_t handle, const char *html);
    int mp_html_to_pdf(const char *html, const char *output_path, uint64_t options);
    int mp_html_file_to_pdf(const char *html_path, const char *output_path, uint64_t options);
    float mp_html_options_get_page_width(uint64_t handle);
    float mp_html_options_get_page_height(uint64_t handle);
    float mp_html_options_get_content_width(uint64_t handle);
    float mp_html_options_get_content_height(uint64_t handle);

    // ============================================================================
    // Document Composition Functions (Platypus-style)
    // ============================================================================

    uint64_t mp_doc_template_create(const char *filename);
    void mp_doc_template_free(uint64_t handle);
    int mp_doc_template_set_page_size(uint64_t handle, float width, float height);
    int mp_doc_template_set_margins(uint64_t handle, float left, float right, float top, float bottom);

    uint64_t mp_frame_create(const char *id, float x, float y, float width, float height);
    void mp_frame_free(uint64_t handle);
    float mp_frame_available_width(uint64_t handle);
    float mp_frame_available_height(uint64_t handle);

    uint64_t mp_story_create(void);
    void mp_story_free(uint64_t handle);
    size_t mp_story_len(uint64_t handle);

    uint64_t mp_paragraph_create(const char *text);
    void mp_paragraph_free(uint64_t handle);
    int mp_paragraph_set_font_size(uint64_t handle, float size);
    int mp_paragraph_set_leading(uint64_t handle, float leading);

    uint64_t mp_spacer_create(float height);
    void mp_spacer_free(uint64_t handle);

    uint64_t mp_table_create(size_t rows, size_t cols);
    void mp_table_free(uint64_t handle);
    size_t mp_table_num_rows(uint64_t handle);
    size_t mp_table_num_cols(uint64_t handle);

    uint64_t mp_table_style_create(void);
    void mp_table_style_free(uint64_t handle);
    uint64_t mp_table_style_add_grid(uint64_t handle, float weight, float r, float g, float b);
    uint64_t mp_table_style_add_background(uint64_t handle, int start_col, int start_row, int end_col, int end_row, float r, float g, float b);

    uint64_t mp_toc_create(void);
    void mp_toc_free(uint64_t handle);
    uint64_t mp_toc_set_title(uint64_t handle, const char *title);
    uint64_t mp_toc_add_entry(uint64_t handle, const char *title, unsigned char level, size_t page);

    uint64_t mp_toc_builder_create(void);
    void mp_toc_builder_free(uint64_t handle);
    int mp_toc_builder_add_heading(uint64_t handle, const char *title, unsigned char level, size_t page);

    uint64_t mp_paragraph_style_create(const char *name);
    void mp_paragraph_style_free(uint64_t handle);
    uint64_t mp_paragraph_style_set_font_size(uint64_t handle, float size);
    uint64_t mp_paragraph_style_set_leading(uint64_t handle, float leading);
    uint64_t mp_paragraph_style_set_alignment(uint64_t handle, int align);

    uint64_t mp_stylesheet_create(void);
    void mp_stylesheet_free(uint64_t handle);
    int mp_stylesheet_add_style(uint64_t sheet_handle, uint64_t style_handle);

    uint64_t mp_image_create(const char *path);
    void mp_image_free(uint64_t handle);
    uint64_t mp_image_set_width(uint64_t handle, float width);
    uint64_t mp_image_set_height(uint64_t handle, float height);

    uint64_t mp_hr_create(void);
    void mp_hr_free(uint64_t handle);
    uint64_t mp_hr_set_thickness(uint64_t handle, float thickness);

    uint64_t mp_list_item_bullet(const char *text);
    uint64_t mp_list_item_numbered(size_t number, const char *text);
    void mp_list_item_free(uint64_t handle);

    // ============================================================================
    // Font Info Functions
    // ============================================================================

    uint64_t mp_pdf_font_from_dict(uint64_t dict_ptr);
    double mp_pdf_font_glyph_width(uint64_t font, unsigned int char_code);
    int mp_pdf_font_decode_text(uint64_t font, const unsigned char *bytes, size_t len, unsigned char *out_buf, size_t out_len);
    void mp_pdf_font_free(uint64_t font);

    // ============================================================================
    // Image Operations Functions
    // ============================================================================

    uint64_t mp_pdf_image_from_dict(uint64_t dict_ptr, const unsigned char *data, size_t data_len);
    unsigned int mp_pdf_image_width(uint64_t img);
    unsigned int mp_pdf_image_height(uint64_t img);
    int mp_pdf_image_to_rgba(uint64_t img, unsigned char *out_buf, size_t out_len);
    void mp_pdf_image_free(uint64_t img);

    // ============================================================================
    // PDF Writer Functions
    // ============================================================================

    uint64_t mp_writer_new(void);
    int mp_writer_add_object(uint64_t writer, int obj_num, unsigned short generation, uint64_t object);
    long long mp_writer_write(uint64_t writer, unsigned char *buf, size_t buf_len);
    void mp_writer_free(uint64_t writer);

    // ============================================================================
    // Office Document Functions (DOCX, XLSX, PPTX)
    // ============================================================================

    uint64_t office_new_document(uint64_t ctx, int doc_type);
    uint64_t office_new_docx(uint64_t ctx);
    uint64_t office_new_xlsx(uint64_t ctx);
    uint64_t office_new_pptx(uint64_t ctx);
    void office_drop_document(uint64_t ctx, uint64_t doc);
    uint64_t office_open_document(uint64_t ctx, const char *filename);

    int office_get_type(uint64_t ctx, uint64_t doc);
    int office_page_count(uint64_t ctx, uint64_t doc);
    int office_get_page_size(uint64_t ctx, uint64_t doc, float *width, float *height);
    int office_set_page_size(uint64_t ctx, uint64_t doc, float width, float height);

    char* office_get_title(uint64_t ctx, uint64_t doc);
    int office_set_title(uint64_t ctx, uint64_t doc, const char *title);
    char* office_get_creator(uint64_t ctx, uint64_t doc);
    int office_set_creator(uint64_t ctx, uint64_t doc, const char *creator);

    int office_add_paragraph(uint64_t ctx, uint64_t doc, const char *text);
    int office_add_heading(uint64_t ctx, uint64_t doc, const char *text, int level);
    int office_content_count(uint64_t ctx, uint64_t doc);

    int office_add_sheet(uint64_t ctx, uint64_t doc, const char *name);
    int office_sheet_count(uint64_t ctx, uint64_t doc);
    char* office_get_sheet_name(uint64_t ctx, uint64_t doc, int sheet_idx);
    int office_set_cell_string(uint64_t ctx, uint64_t doc, int sheet_idx, int row, int col, const char *value);
    int office_set_cell_number(uint64_t ctx, uint64_t doc, int sheet_idx, int row, int col, double value);
    char* office_get_cell_string(uint64_t ctx, uint64_t doc, int sheet_idx, int row, int col);

    int office_add_slide(uint64_t ctx, uint64_t doc);
    int office_slide_count(uint64_t ctx, uint64_t doc);
    int office_set_slide_title(uint64_t ctx, uint64_t doc, int slide_num, const char *title);
    char* office_get_slide_title(uint64_t ctx, uint64_t doc, int slide_num);

    void office_free_string(char *s);
    char* office_type_name(uint64_t ctx, int doc_type);
    char* office_type_extension(uint64_t ctx, int doc_type);

    // ============================================================================
    // Convenience Functions - High-level wrappers for common PDF operations
    // ============================================================================

    // Result structures
    typedef struct {
        int32_t page_count;
        int32_t is_encrypted;
        int32_t needs_password;
        char* version;
        char* title;
        char* author;
        char* subject;
        char* creator;
    } MpPdfInfo;

    typedef struct {
        float width;
        float height;
    } MpPageDimensions;

    typedef struct {
        uint8_t* data;
        size_t data_len;
        int32_t width;
        int32_t height;
    } MpRenderedPage;

    typedef struct {
        char* text;
        size_t text_len;
        int32_t pages_processed;
    } MpExtractedText;

    // Document information
    int32_t mp_get_pdf_info(const char* pdf_path, MpPdfInfo* info_out);
    void mp_free_pdf_info(MpPdfInfo* info);
    int32_t mp_get_page_count(const char* pdf_path);
    int32_t mp_get_page_dimensions(const char* pdf_path, int32_t page_num, MpPageDimensions* dims_out);

    // Text extraction
    int32_t mp_extract_text(const char* pdf_path, MpExtractedText* result_out);
    char* mp_extract_page_text(const char* pdf_path, int32_t page_num);
    void mp_free_extracted_text(MpExtractedText* result);

    // Page rendering
    int32_t mp_render_page_to_png(const char* pdf_path, int32_t page_num, float scale, MpRenderedPage* result_out);
    int32_t mp_render_page_to_rgb(const char* pdf_path, int32_t page_num, float scale, MpRenderedPage* result_out);
    void mp_free_rendered_page(MpRenderedPage* result);

    // File operations
    int32_t mp_merge_pdf_files(const char* const* input_paths, int32_t input_count, const char* output_path);
    int32_t mp_split_pdf_to_pages(const char* pdf_path, const char* output_dir);
    int32_t mp_copy_pages(const char* pdf_path, const char* output_path, const int32_t* page_numbers, int32_t page_count);

    // Validation and repair
    int32_t mp_is_valid_pdf(const char* pdf_path);
    int32_t mp_repair_damaged_pdf(const char* pdf_path, const char* output_path);

    // Memory management for convenience functions
    void mp_free_bytes(uint8_t* data, size_t len);
""")

# =============================================================================
# Constants
# =============================================================================

FZ_STORE_DEFAULT = 256 * 1024 * 1024  # 256 MB

# =============================================================================
# Library Loading
# =============================================================================

class MockLib:
    """Mock library that raises errors when functions are called."""

    def __getattr__(self, name: str):
        def mock_func(*args, **kwargs):
            raise RuntimeError(
                f"Function '{name}' requires native library. "
                "Please install micropdf with native bindings or set MICROPDF_LIB_PATH."
            )
        return mock_func


def _load_ffi_library() -> object:
    """Load the FFI library, returning MockLib if not available."""
    lib_path = get_library_path()

    if lib_path is not None:
        try:
            return ffi.dlopen(str(lib_path))
        except OSError:
            pass

    return MockLib()


# Load library at module import
lib = _load_ffi_library()


def is_native_available() -> bool:
    """Check if the native library is available."""
    return not isinstance(lib, MockLib)


def reload_library() -> bool:
    """Reload the native library. Returns True if successful."""
    global lib, _library_path
    _library_path = None
    lib = _load_ffi_library()
    return is_native_available()


# =============================================================================
# Exports
# =============================================================================

__all__ = [
    # FFI objects
    "ffi",
    "lib",
    # Constants
    "FZ_STORE_DEFAULT",
    # Platform functions
    "get_platform_info",
    "get_lib_name",
    # Library functions
    "find_library",
    "download_library",
    "load_library",
    "get_library_path",
    "is_native_available",
    "reload_library",
    # Classes
    "MockLib",
]
