<?php
/**
 * Extract and parse dish rows from menu PDF files.
 *
 * @package Maca_Menu_List
 */

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Whether shell_exec can be used on this host.
 *
 * @return bool
 */
function maca_menulist_pdf_shell_allowed() {
    if (!function_exists('shell_exec')) {
        return false;
    }

    $disabled = ini_get('disable_functions');

    if (!is_string($disabled) || $disabled === '') {
        return true;
    }

    $disabled_list = array_map('trim', explode(',', strtolower($disabled)));

    return !in_array('shell_exec', $disabled_list, true);
}

/**
 * Whether the bundled smalot/pdfparser library can be loaded.
 *
 * @return bool
 */
function maca_menulist_pdf_parser_ready() {
    static $ready = null;

    if ($ready !== null) {
        return $ready;
    }

    $autoload = MACA_MENULIST_PLUGIN_DIR . 'vendor/smalot/pdfparser/alt_autoload.php';

    $ready = is_readable($autoload)
        && extension_loaded('zlib')
        && extension_loaded('iconv')
        && extension_loaded('mbstring');

    return $ready;
}

/**
 * Load bundled PDF parser (heavy — call only during import).
 *
 * @return bool
 */
function maca_menulist_pdf_load_parser() {
    static $loaded = false;

    if ($loaded) {
        return true;
    }

    if (!maca_menulist_pdf_parser_ready()) {
        return false;
    }

    require_once MACA_MENULIST_PLUGIN_DIR . 'vendor/smalot/pdfparser/alt_autoload.php';
    $loaded = true;

    return true;
}

/**
 * Back-compat alias.
 *
 * @return bool
 */
function maca_menulist_pdf_parser_available() {
    return maca_menulist_pdf_parser_ready();
}

/**
 * Raise time/memory limits for PDF parsing (best effort).
 *
 * @return void
 */
function maca_menulist_pdf_import_raise_limits() {
    if (function_exists('wp_raise_memory_limit')) {
        wp_raise_memory_limit('admin');
    }

    if (function_exists('set_time_limit')) {
        // phpcs:ignore WordPress.PHP.NoSilencedFunctions.Discouraged
        @set_time_limit(120);
    }
}

/**
 * Resolve a media/menu file URL to a readable local path.
 *
 * @param string $file_url File URL.
 * @return string Empty string when not resolvable.
 */
function maca_menulist_pdf_resolve_local_path($file_url) {
    $file_url = trim((string) $file_url);

    if ($file_url === '') {
        return '';
    }

    $upload_dir = wp_upload_dir();

    if (!empty($upload_dir['baseurl']) && !empty($upload_dir['basedir'])) {
        $baseurl = trailingslashit($upload_dir['baseurl']);
        $basedir = trailingslashit($upload_dir['basedir']);

        if (strpos($file_url, $baseurl) === 0) {
            $relative = ltrim(substr($file_url, strlen($baseurl)), '/');
            $path = $basedir . str_replace('/', DIRECTORY_SEPARATOR, $relative);

            if (is_readable($path)) {
                return $path;
            }
        }
    }

    $attachment_id = attachment_url_to_postid($file_url);

    if ($attachment_id > 0) {
        $path = get_attached_file($attachment_id);

        if (is_string($path) && $path !== '' && is_readable($path)) {
            return $path;
        }
    }

    return '';
}

/**
 * Extract plain text from a PDF file.
 *
 * @param string $file_path Absolute path to PDF.
 * @return string|WP_Error
 */
function maca_menulist_pdf_extract_text($file_path) {
    if (!is_readable($file_path)) {
        return new WP_Error('pdf_missing', __('PDF file could not be read.', 'maca-restu-pro'));
    }

    $max_bytes = 15 * 1024 * 1024;
    $size = filesize($file_path);

    if (is_int($size) && $size > $max_bytes) {
        return new WP_Error(
            'pdf_too_large',
            __('PDF file is too large to analyze (max 15 MB).', 'maca-restu-pro')
        );
    }

    maca_menulist_pdf_import_raise_limits();

    $text = maca_menulist_pdf_extract_text_via_pdftotext($file_path);

    if (is_string($text) && $text !== '') {
        return $text;
    }

    $text = maca_menulist_pdf_extract_text_via_smalot($file_path);

    if (is_string($text) && $text !== '') {
        return $text;
    }

    if (is_wp_error($text)) {
        return $text;
    }

    return new WP_Error(
        'pdf_extract_failed',
        __('Could not extract text from the PDF. Use a PDF with selectable text (not a scanned image).', 'maca-restu-pro')
    );
}

/**
 * Extract text using pdftotext CLI when available (lightweight).
 *
 * @param string $file_path Absolute path to PDF.
 * @return string Empty string on failure.
 */
function maca_menulist_pdf_extract_text_via_pdftotext($file_path) {
    if (!maca_menulist_pdf_shell_allowed() || !maca_menulist_pdf_command_exists('pdftotext')) {
        return '';
    }

    $escaped = escapeshellarg($file_path);
    $stderr = stripos(PHP_OS, 'WIN') === 0 ? '' : ' 2>/dev/null';
    // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.shell_exec_shell_exec
    $output = shell_exec('pdftotext -layout ' . $escaped . ' -' . $stderr);

    if (!is_string($output) || trim($output) === '') {
        return '';
    }

    return maca_menulist_pdf_normalize_extracted_text($output);
}

/**
 * Extract text using bundled smalot/pdfparser.
 *
 * @param string $file_path Absolute path to PDF.
 * @return string|WP_Error Empty string or error on failure.
 */
function maca_menulist_pdf_extract_text_via_smalot($file_path) {
    if (!maca_menulist_pdf_load_parser()) {
        return '';
    }

    try {
        $config = new Smalot\PdfParser\Config();
        $config->setFontSpaceLimit(-60);
        $config->setHorizontalOffset("\t");
        $config->setRetainImageContent(false);
        $config->setDecodeMemoryLimit(1000000);

        $parser = new Smalot\PdfParser\Parser(array(), $config);
        $pdf = $parser->parseFile($file_path);
        $text = trim((string) $pdf->getText());

        if ($text === '') {
            return '';
        }

        return maca_menulist_pdf_normalize_extracted_text($text);
    } catch (Throwable $e) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
        if (function_exists('maca_menulist_debug_log')) {
            maca_menulist_debug_log('PDF import smalot: ' . $e->getMessage());
        }

        return new WP_Error(
            'pdf_parser_error',
            __('PDF parsing failed on the server. Try a smaller PDF or contact your host.', 'maca-restu-pro')
        );
    }
}

/**
 * Whether a shell command exists.
 *
 * @param string $command Command name.
 * @return bool
 */
function maca_menulist_pdf_command_exists($command) {
    if (!maca_menulist_pdf_shell_allowed()) {
        return false;
    }

    if (stripos(PHP_OS, 'WIN') === 0) {
        // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.shell_exec_shell_exec
        $result = shell_exec('where ' . escapeshellarg($command) . ' 2>nul');
    } else {
        // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.shell_exec_shell_exec
        $result = shell_exec('command -v ' . escapeshellarg($command) . ' 2>/dev/null');
    }

    return is_string($result) && trim($result) !== '';
}

/**
 * Store an admin notice for the next redirect.
 *
 * @param string $message Message.
 * @param string $type success|error|info.
 * @return void
 */
function maca_menulist_pdf_import_flash_notice($message, $type = 'success') {
    set_transient(
        'maca_pdf_import_flash_' . get_current_user_id(),
        array(
            'message' => $message,
            'type' => $type,
        ),
        60
    );
}

/**
 * Show flashed admin notice after redirect.
 *
 * @return void
 */
function maca_menulist_pdf_import_show_flash_notice() {
    $flash = get_transient('maca_pdf_import_flash_' . get_current_user_id());

    if (!is_array($flash) || empty($flash['message'])) {
        return;
    }

    delete_transient('maca_pdf_import_flash_' . get_current_user_id());

    $type = isset($flash['type']) ? (string) $flash['type'] : 'success';
    $level = ($type === 'success') ? 'updated' : 'error';

    add_settings_error('maca_pdf_import', $type, $flash['message'], $level);
}

/**
 * Normalize line breaks and stray spacing from PDF extractors.
 *
 * @param string $text Raw text.
 * @return string
 */
function maca_menulist_pdf_normalize_extracted_text($text) {
    $text = str_replace(array("\r\n", "\r"), "\n", $text);
    $lines = explode("\n", $text);
    $normalized = array();

    foreach ($lines as $line) {
        $line = trim($line);

        if ($line === '') {
            continue;
        }

        if (preg_match('/(?:\S\s){4,}\S/u', $line)) {
            $line = preg_replace('/(?<=\S)\s+(?=\S)/u', '', $line);
        }

        $line = preg_replace('/\s{2,}/u', ' ', $line);
        $normalized[] = $line;
    }

    return implode("\n", $normalized);
}

/**
 * Whether a line is likely a category heading (not a dish or description).
 *
 * @param string $line Single line.
 * @return bool
 */
function maca_menulist_pdf_line_looks_like_category($line) {
    if ($line === '' || preg_match('/\d/u', $line)) {
        return false;
    }

    if (preg_match('/^med\s+/iu', $line) || preg_match('/^och\s+/iu', $line)) {
        return false;
    }

    if (strpos($line, ',') !== false) {
        return false;
    }

    if (!preg_match('/^\p{Lu}/u', $line)) {
        return false;
    }

    if (strlen($line) > 48) {
        return false;
    }

    if (preg_match('/(?:KR|kr)\s*$/u', $line)) {
        return false;
    }

    $word_count = count(preg_split('/\s+/u', $line, -1, PREG_SPLIT_NO_EMPTY));

    if ($word_count > 4) {
        return false;
    }

    return (bool) preg_match('/^[\p{L}\p{N}\s\-&:\'\/\.]+$/u', $line);
}

/**
 * Parse menu plain text into dish import rows.
 *
 * @param string $text Extracted PDF text.
 * @return array<int, array<string, mixed>>
 */
function maca_menulist_pdf_parse_menu_text($text) {
    $lines = preg_split('/\r\n|\r|\n/', (string) $text);
    $lines = array_map('trim', $lines);
    $lines = array_values(
        array_filter(
            $lines,
            static function ($line) {
                if ($line === '') {
                    return false;
                }

                return !preg_match('/^Meny\s+[\d\-\/]+/iu', $line);
            }
        )
    );

    $price_patterns = array(
        '/^(.+?)\s+(\d{1,5})\s*(?:KR|kr)\s*$/u',
        '/^(.+?)(\d{2,5})\s*(?:KR|kr)\s*$/u',
    );

    $category = '';
    $rows = array();
    $current = null;

    foreach ($lines as $line) {
        $matched_price = false;

        foreach ($price_patterns as $pattern) {
            if (preg_match($pattern, $line, $matches)) {
                if ($current !== null) {
                    $rows[] = maca_menulist_pdf_finalize_import_row($current);
                }

                $current = array(
                    'category_name' => $category,
                    'title' => trim($matches[1]),
                    'description' => '',
                    'price' => (float) $matches[2],
                    'import' => true,
                );
                $matched_price = true;
                break;
            }
        }

        if ($matched_price) {
            continue;
        }

        if (preg_match('/^med\s+/iu', $line)) {
            if ($current !== null) {
                $current['description'] = maca_menulist_pdf_append_description($current['description'], $line);
            }
            continue;
        }

        if (maca_menulist_pdf_line_looks_like_category($line)) {
            if ($current !== null) {
                $rows[] = maca_menulist_pdf_finalize_import_row($current);
                $current = null;
            }

            $category = $line;
            continue;
        }

        if ($current !== null) {
            $current['description'] = maca_menulist_pdf_append_description($current['description'], $line);
        }
    }

    if ($current !== null) {
        $rows[] = maca_menulist_pdf_finalize_import_row($current);
    }

    return $rows;
}

/**
 * @param string $existing Existing description.
 * @param string $line New line.
 * @return string
 */
function maca_menulist_pdf_append_description($existing, $line) {
    $line = trim($line);

    if ($line === '') {
        return $existing;
    }

    if ($existing === '') {
        return $line;
    }

    return $existing . ' ' . $line;
}

/**
 * @param array<string, mixed> $row Raw row.
 * @return array<string, mixed>
 */
function maca_menulist_pdf_finalize_import_row($row) {
    $row['title'] = trim((string) $row['title']);
    $row['category_name'] = trim((string) $row['category_name']);
    $row['description'] = maca_menulist_pdf_clean_description((string) $row['description']);

    if ($row['category_name'] === '') {
        $row['category_name'] = __('Uncategorized', 'maca-restu-pro');
    }

    return $row;
}

/**
 * Remove category labels accidentally merged into descriptions.
 *
 * @param string $description Description text.
 * @return string
 */
function maca_menulist_pdf_clean_description($description) {
    $description = trim(preg_replace('/\s+/u', ' ', $description));

    if ($description === '') {
        return '';
    }

    $description = preg_replace('/\s+(Dessert|Snacks|Tapas)\s*$/iu', '', $description);
    $description = preg_replace('/\s+(Dessert\s+Snacks)\s*$/iu', '', $description);

    return trim($description);
}

/**
 * Transient key for preview data.
 *
 * @return string
 */
function maca_menulist_pdf_import_transient_key() {
    return 'maca_pdf_import_' . get_current_user_id();
}

/**
 * @return array<string, mixed>|null
 */
function maca_menulist_pdf_import_get_preview() {
    $data = get_transient(maca_menulist_pdf_import_transient_key());

    return is_array($data) ? $data : null;
}

/**
 * @param array<string, mixed> $data Preview payload.
 * @return void
 */
function maca_menulist_pdf_import_set_preview($data) {
    set_transient(maca_menulist_pdf_import_transient_key(), $data, HOUR_IN_SECONDS);
}

/**
 * @return void
 */
function maca_menulist_pdf_import_clear_preview() {
    delete_transient(maca_menulist_pdf_import_transient_key());
}

/**
 * Published PDF menus for optional import source picker.
 *
 * @return array<int, object>
 */
function maca_menulist_pdf_import_get_pdf_menus() {
    global $wpdb;

    $menus_table = maca_menulist_db_menus_table();
    $categories_table = maca_menulist_db_categories_table();

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names from $wpdb->prefix helpers.
    return $wpdb->get_results(
        "SELECT m.id, m.title, m.file_url, c.name AS category_name
        FROM $menus_table m
        LEFT JOIN $categories_table c ON m.category_id = c.id
        WHERE m.file_type = 'pdf' AND m.file_url != ''
        ORDER BY m.title ASC"
    );
}

/**
 * Resolve or create dish category ID by name.
 *
 * @param string $name Category name.
 * @param bool   $create_missing Whether to insert when missing.
 * @param array<string, int> $cache Name => ID cache (by reference).
 * @return int Category ID or 0.
 */
function maca_menulist_pdf_import_resolve_category_id($name, $create_missing, &$cache) {
    $name = trim($name);

    if ($name === '') {
        return 0;
    }

    $key = mb_strtolower($name, 'UTF-8');

    if (isset($cache[$key])) {
        return (int) $cache[$key];
    }

    foreach (maca_menulist_db_get_dish_categories() as $category) {
        $labels = array(
            trim((string) $category->name),
            trim((string) ($category->name_en ?? '')),
        );

        foreach ($labels as $label) {
            if ($label !== '' && mb_strtolower($label, 'UTF-8') === $key) {
                $cache[$key] = (int) $category->id;
                return (int) $category->id;
            }
        }
    }

    if (!$create_missing) {
        return 0;
    }

    global $wpdb;
    $table = maca_menulist_db_dish_categories_table();
    $max_sort = $wpdb->get_var("SELECT MAX(sort_order) FROM $table");
    $sort_order = ($max_sort !== null && $max_sort !== '') ? ((int) $max_sort + 1) : 0;

    $inserted = maca_menulist_db_insert_dish_category(
        array(
            'name' => $name,
            'name_en' => '',
            'sort_order' => $sort_order,
        )
    );

    if ($inserted === false) {
        return 0;
    }

    $category_id = (int) $wpdb->insert_id;
    $cache[$key] = $category_id;

    return $category_id;
}

/**
 * Import parsed rows into dish tables.
 *
 * @param array<int, array<string, mixed>> $rows Rows from preview form.
 * @param bool                             $create_categories Create missing dish categories.
 * @return array{imported: int, skipped: int, errors: array<int, string>}
 */
function maca_menulist_pdf_import_save_rows($rows, $create_categories = true) {
    $result = array(
        'imported' => 0,
        'skipped' => 0,
        'errors' => array(),
    );

    $category_cache = array();
    $sort_by_category = array();

    foreach ($rows as $index => $row) {
        if (empty($row['import'])) {
            ++$result['skipped'];
            continue;
        }

        $title = sanitize_text_field((string) ($row['title'] ?? ''));
        $description = sanitize_textarea_field((string) ($row['description'] ?? ''));
        $category_name = sanitize_text_field((string) ($row['category_name'] ?? ''));
        $price_raw = isset($row['price']) ? wp_unslash((string) $row['price']) : '';
        $price_raw = str_replace(',', '.', trim($price_raw));
        $price = ($price_raw !== '') ? floatval($price_raw) : null;

        if ($title === '') {
            $result['errors'][$index] = __('Dish title is required.', 'maca-restu-pro');
            continue;
        }

        $category_id = maca_menulist_pdf_import_resolve_category_id($category_name, $create_categories, $category_cache);

        if ($category_id <= 0) {
            $result['errors'][$index] = __('Could not resolve dish category.', 'maca-restu-pro');
            continue;
        }

        if (!isset($sort_by_category[$category_id])) {
            global $wpdb;
            $dishes_table = maca_menulist_db_dishes_table();
            $max_sort = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT MAX(sort_order) FROM $dishes_table WHERE category_id = %d",
                    $category_id
                )
            );
            $sort_by_category[$category_id] = ($max_sort !== null && $max_sort !== '') ? (int) $max_sort : -1;
        }

        ++$sort_by_category[$category_id];

        $inserted = maca_menulist_db_insert_dish(
            array(
                'category_id' => $category_id,
                'title' => $title,
                'title_en' => '',
                'description' => $description,
                'description_en' => '',
                'price' => $price,
                'price_takeaway' => null,
                'takeaway_active' => 0,
                'vat_code' => 'food',
                'image_url' => null,
                'sort_order' => $sort_by_category[$category_id],
                'is_active' => 1,
            )
        );

        if ($inserted === false) {
            $result['errors'][$index] = __('Database error while saving dish.', 'maca-restu-pro');
            continue;
        }

        ++$result['imported'];
    }

    return $result;
}
