<?php
/**
 * Forge Suite Dashboard — card renderers + stat collectors.
 *
 * F13 phase 4 extraction (2026-04-21). Split from class-forge-dashboard.php
 * to cut the facade down from 1235 lines and put card-specific render/
 * stat logic into its own file.
 *
 * Shared across all 4 Forge plugins — first-loaded plugin wins via
 * bulletproof `if (!class_exists)` wrap (PHP-8.3-safe after recipes
 * 2026-04-21 incident).
 *
 * Everything here is static + side-effect-free render/query code. No
 * constructor. No hooks. Just echoes markup OR returns stats arrays.
 *
 * @package ForgeSuite
 */

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

if (!class_exists('Forge_Suite_Dashboard_Cards')) {

class Forge_Suite_Dashboard_Cards {

    // ═════════════════════════════════════════════════════════════════
    // Stats collectors
    // ═════════════════════════════════════════════════════════════════

    /**
 * Pull SEO Forge's own per-post analysis stats. Bails silently if
 * SEO Forge isn't active — the dashboard falls back to "No data yet".
 */
    public static function get_seo_stats() {
        if (!class_exists('SEOFORGE_Dashboard')) return null;
        try {
            return SEOFORGE_Dashboard::instance()->get_stats();
        } catch (\Exception $e) {
            // Silent fallback to "No data yet" — but log so we know if a
            // refactor in SEOForge starts throwing. Independent review
            // 2026-05-26 (MEDIUM-Code #18): silent catches hid signal.

            return null;
        }
    }

    /**
 * Walk wp_lf_translations + wp_posts to build the Lang Forge card's
 * shape:
 * total_translations, lang_count, source_posts, default_lang,
 * per_language => [code => ['published','draft','missing',...]]
 *
 * note: we clamp per-language numerators at source_posts so a
 * duplicated translation row can't push the percentage > 100%.
 */
    public static function get_lang_stats() {
        if (!class_exists('LF_Core')) return null;
        global $wpdb;

        $table = $wpdb->prefix . 'lf_translations';
        if ($wpdb->get_var("SHOW TABLES LIKE '$table'") !== $table) return null; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table; direct query intended.

        $active_langs = [];
        $default_lang = 'en';
        $translatable_types = ['post', 'page'];
        if (class_exists('LF_Core')) {
            try {
                $active_langs = LF_Core::instance()->get_active_languages();
                $default_lang = LF_Core::instance()->get_default_language();
                $types = LF_Core::get_translatable_post_types();
                if (!empty($types)) $translatable_types = $types;
            } catch (\Exception $e) {
                // Fall back to default ['post','page'] but log so a future
                // breaking change in LF_Core surfaces. (MEDIUM-Code #18.)

            }
        }

        $type_placeholders = implode(',', array_fill(0, count($translatable_types), '%s'));

        // Total = translations for translatable post types only (excludes attachment, acf-field, nav_menu_item, etc.)
        $total = (int) $wpdb->get_var($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table; direct query intended.
            "SELECT COUNT(*) FROM $table WHERE element_type IN ($type_placeholders)",
            ...$translatable_types
        ));

        // Count source posts (default language, published only)
        $source_posts = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} p
             INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_lf_language'
             WHERE p.post_status = 'publish' AND p.post_type IN ($type_placeholders) AND pm.meta_value = %s",
            ...array_merge($translatable_types, [$default_lang])
        ));
        $source_posts += (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->posts} p
             LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_lf_language'
             WHERE p.post_status = 'publish' AND p.post_type IN ($type_placeholders) AND pm.meta_value IS NULL",
            ...$translatable_types
        ));

        // Per-language: published, draft, missing
        $per_language = [];
        $target_langs = array_filter($active_langs, function($code) use ($default_lang) {
            return $code !== $default_lang;
        }, ARRAY_FILTER_USE_KEY);

        foreach ($target_langs as $code => $lang) {
            // cap numerator at source_posts so a malformed
            // wp_lf_translations row (duplicate per post, or translation
            // without a default-language source) can't push pct over 100%.
            // Also: COUNT(DISTINCT p.ID) on posts so translations to
            // already-deleted posts don't bloat the count.
            $published = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT p.ID) FROM $table t
                 INNER JOIN {$wpdb->posts} p ON t.element_id = p.ID
                 WHERE t.language_code = %s AND t.element_type IN ($type_placeholders) AND p.post_status = 'publish'",
                ...array_merge([$code], $translatable_types)
            ));
            $draft = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT p.ID) FROM $table t
                 INNER JOIN {$wpdb->posts} p ON t.element_id = p.ID
                 WHERE t.language_code = %s AND t.element_type IN ($type_placeholders) AND p.post_status = 'draft'",
                ...array_merge([$code], $translatable_types)
            ));
            // Clamp counts to the source-post denominator — a translation
            // can never represent more than 100% of the sources.
            $published = min($published, $source_posts);
            $draft     = min($draft, max(0, $source_posts - $published));
            $missing   = max(0, $source_posts - $published - $draft);

            $pct_published = $source_posts > 0 ? min(100, round(($published / $source_posts) * 100)) : 0;
            $pct_draft     = $source_posts > 0 ? min(100 - $pct_published, round(($draft / $source_posts) * 100)) : 0;
            $pct_total     = min(100, $pct_published + $pct_draft);

            $per_language[$code] = [
                'code'          => $code,
                'name'          => $lang['native_name'] ?? $lang['name'] ?? $code,
                'flag'          => $lang['flag'] ?? $code,
                'published'     => $published,
                'draft'         => $draft,
                'missing'       => $missing,
                'total'         => $source_posts,
                'pct_published' => $pct_published,
                'pct_draft'     => $pct_draft,
                'pct_total'     => $pct_total,
            ];
        }

        return [
            'total_translations' => $total,
            'lang_count'         => count($target_langs),
            'source_posts'       => $source_posts,
            'default_lang'       => $default_lang,
            'per_language'       => $per_language,
        ];
    }

    /**
 * Form Forge: active forms, last-30d submissions, conversion rate.
 * Requires the formforge tables — returns null when Form Forge is
 * inactive or hasn't migrated yet.
 */
    public static function get_form_stats() {
        if (!class_exists('FORMFORGE_Core')) return null;
        global $wpdb;

        $forms_table = $wpdb->prefix . 'formforge_forms';
        if ($wpdb->get_var("SHOW TABLES LIKE '$forms_table'") !== $forms_table) return null;

        $active_forms = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM $forms_table WHERE status = 'active'"
        );
        $total_forms = (int) $wpdb->get_var("SELECT COUNT(*) FROM $forms_table");

        $subs_table = $wpdb->prefix . 'formforge_submissions';
        $analytics_table = $wpdb->prefix . 'formforge_analytics';
        $since = date('Y-m-d H:i:s', strtotime('-30 days'));

        $submissions_month = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $subs_table WHERE created_at >= %s", $since
        ));

        $agg = $wpdb->get_row($wpdb->prepare(
            "SELECT
                SUM(CASE WHEN event_type = 'view' THEN 1 ELSE 0 END) AS views
            FROM $analytics_table WHERE created_at >= %s",
            $since
        ));
        $views = (int) ($agg->views ?? 0);
        $rate = $views > 0 ? round(($submissions_month / $views) * 100, 1) : 0;

        return [
            'active_forms'       => $active_forms,
            'total_forms'        => $total_forms,
            'submissions_month'  => $submissions_month,
            'conversion_rate'    => $rate,
            'views_month'        => $views,
        ];
    }

    /**
 * Field Forge: field-group count, total field values, posts-with-fields.
 * Returns null if Field Forge isn't active.
 */
    public static function get_fieldforge_stats() {
        if (!class_exists('FIELDFORGE_Core')) return null;
        global $wpdb;

        $groups_table = $wpdb->prefix . 'fieldforge_field_groups';
        if ($wpdb->get_var("SHOW TABLES LIKE '$groups_table'") !== $groups_table) return null;

        $total_groups = (int) $wpdb->get_var("SELECT COUNT(*) FROM $groups_table WHERE status = 'publish'");

        $values_table = $wpdb->prefix . 'fieldforge_values';
        $total_values = 0;
        $posts_with_fields = 0;
        if ($wpdb->get_var("SHOW TABLES LIKE '$values_table'") === $values_table) {
            $total_values = (int) $wpdb->get_var("SELECT COUNT(*) FROM $values_table");
            $posts_with_fields = (int) $wpdb->get_var("SELECT COUNT(DISTINCT post_id) FROM $values_table");
        }

        return [
            'total_groups'      => $total_groups,
            'total_values'      => $total_values,
            'posts_with_fields' => $posts_with_fields,
        ];
    }

    // ═════════════════════════════════════════════════════════════════
    // Card renderers — active / inactive / not-installed variants
    // ═════════════════════════════════════════════════════════════════

    public static function render_active_card($key, $plugin, $stats) {
        ?>
        <div class="forge-suite-card forge-suite-card--<?php echo esc_attr($key); ?>">
            <div class="forge-suite-card-header">
                <?php echo Forge_Suite_Dashboard::logo_img($plugin['slug'], 'forge-suite-logo forge-suite-logo--header'); ?>
                <h3><?php echo esc_html($plugin['name']); ?></h3>
            </div>
            <div class="forge-suite-card-body">
                <?php
                switch ($key) {
                    case 'seo':
                        self::render_seo_card_body($stats);
                        break;
                    case 'lang':
                        self::render_lang_card_body($stats);
                        break;
                    case 'form':
                        self::render_form_card_body($stats);
                        break;
                    case 'fieldforge':
                        self::render_fieldforge_card_body($stats);
                        break;
                }
                ?>
            </div>
            <div class="forge-suite-card-footer">
                <a href="<?php echo esc_url($plugin['admin_url']); ?>">
                    <?php echo $key === 'fieldforge' ? esc_html__('View Field Groups', 'seoforge') : esc_html__('View Dashboard', 'seoforge'); ?> &rarr;
                </a>
            </div>
        </div>
        <?php
    }

    public static function render_inactive_card($plugin) {
        ?>
        <div class="forge-suite-card forge-suite-card--inactive">
            <div class="forge-suite-card-header">
                <?php echo Forge_Suite_Dashboard::logo_img($plugin['slug'], 'forge-suite-logo forge-suite-logo--header forge-suite-logo--muted'); ?>
                <h3 class="forge-suite-card-title--muted"><?php echo esc_html($plugin['name']); ?></h3>
                <span class="forge-suite-badge forge-suite-badge--inactive"><?php esc_html_e('Inactive', 'seoforge'); ?></span>
            </div>
            <div class="forge-suite-card-body forge-suite-card-body--muted">
                <div class="forge-suite-inactive-icon">
                    <?php echo Forge_Suite_Dashboard::logo_img($plugin['slug'], 'forge-suite-logo forge-suite-logo--large forge-suite-logo--muted'); ?>
                </div>
                <p class="forge-suite-inactive-desc"><?php echo esc_html($plugin['description']); ?></p>
            </div>
            <div class="forge-suite-card-footer forge-suite-card-footer--action">
                <a href="<?php echo esc_url($plugin['activate_url']); ?>" class="forge-suite-btn" style="background:<?php echo esc_attr($plugin['color']); ?>;">
                    <?php esc_html_e('Activate', 'seoforge'); ?>
                </a>
            </div>
        </div>
        <?php
    }

    public static function render_not_installed_card($plugin) {
        ?>
        <div class="forge-suite-card forge-suite-card--not-installed">
            <div class="forge-suite-card-header">
                <?php echo Forge_Suite_Dashboard::logo_img($plugin['slug'], 'forge-suite-logo forge-suite-logo--header forge-suite-logo--muted'); ?>
                <h3 class="forge-suite-card-title--muted"><?php echo esc_html($plugin['name']); ?></h3>
                <span class="forge-suite-badge forge-suite-badge--not-installed"><?php esc_html_e('Not Installed', 'seoforge'); ?></span>
            </div>
            <div class="forge-suite-card-body forge-suite-card-body--muted">
                <div class="forge-suite-inactive-icon">
                    <?php echo Forge_Suite_Dashboard::logo_img($plugin['slug'], 'forge-suite-logo forge-suite-logo--large forge-suite-logo--muted'); ?>
                </div>
                <p class="forge-suite-inactive-desc"><?php echo esc_html($plugin['description']); ?></p>
            </div>
            <div class="forge-suite-card-footer forge-suite-card-footer--action">
                <a href="<?php echo esc_url($plugin['install_url']); ?>" class="forge-suite-btn forge-suite-btn--outline" style="color:<?php echo esc_attr($plugin['color']); ?>;border-color:<?php echo esc_attr($plugin['color']); ?>;background:#fff;">
                    <?php esc_html_e('Install', 'seoforge'); ?>
                </a>
            </div>
        </div>
        <?php
    }

    // ═════════════════════════════════════════════════════════════════
    // Per-plugin card body renderers (active state)
    // ═════════════════════════════════════════════════════════════════

    public static function render_seo_card_body($stats) {
        $color = $stats['avg_score'] >= 80 ? '#059669' : ($stats['avg_score'] >= 50 ? '#D97706' : '#DC2626');
        ?>
        <div class="forge-suite-big-number" style="color:<?php echo esc_attr($color); ?>;"><?php echo esc_html($stats['avg_score']); ?></div>
        <div class="forge-suite-big-label"><?php esc_html_e('Average Score', 'seoforge'); ?></div>
        <div class="forge-suite-pie-row">
            <span class="forge-suite-pill" style="background:#ECFDF5;color:#059669;"><?php echo esc_html($stats['good']); ?> good</span>
            <span class="forge-suite-pill" style="background:#FFFBEB;color:#D97706;"><?php echo esc_html($stats['medium']); ?> mid</span>
            <span class="forge-suite-pill" style="background:#FEF2F2;color:#DC2626;"><?php echo esc_html($stats['bad']); ?> bad</span>
        </div>
        <div class="forge-suite-card-meta"><?php echo esc_html($stats['total_analyzed']); ?> <?php esc_html_e('pages analyzed', 'seoforge'); ?></div>
        <?php
    }

    public static function render_lang_card_body($stats) {
        $compact = count($stats['per_language'] ?? []) > 2;
        ?>
        <div class="forge-suite-big-number" style="color:#0EC49E;"><?php echo esc_html($stats['total_translations']); ?></div>
        <div class="forge-suite-big-label"><?php esc_html_e('Total Translations', 'seoforge'); ?></div>
        <?php if (!empty($stats['per_language'])): ?>
            <?php if ($compact): ?>
                <!-- Compact: flag + percentage for 3+ languages -->
                <div style="display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;">
                <?php foreach ($stats['per_language'] as $lang):
                    $bar_color = $lang['pct_published'] >= 80 ? '#059669' : ($lang['pct_total'] >= 50 ? '#D97706' : '#94A3B8');
                ?>
                    <div style="flex:1;min-width:60px;" title="<?php echo esc_attr($lang['name']); ?>: <?php echo esc_attr($lang['published']); ?> published, <?php echo esc_attr($lang['draft']); ?> draft, <?php echo esc_attr($lang['missing']); ?> missing (of <?php echo esc_attr($lang['total']); ?>)">
                        <div style="text-align:center;font-size:14px;margin-bottom:2px;">
                            <?php self::render_flag_img($lang['flag'], 'sm', $lang['name']); ?>
                            <span style="color:#64748b;"><?php echo esc_html($lang['pct_published']); ?>%</span>
                        </div>
                        <?php self::render_lang_progress_bar($lang); ?>
                    </div>
                <?php endforeach; ?>
                </div>
            <?php else: ?>
                <!-- Full: flag + name + stats for 1-2 languages -->
                <?php foreach ($stats['per_language'] as $lang): ?>
                <div style="margin-top:8px;" title="<?php echo esc_attr($lang['published']); ?> published, <?php echo esc_attr($lang['draft']); ?> draft, <?php echo esc_attr($lang['missing']); ?> missing (of <?php echo esc_attr($lang['total']); ?>)">
                    <div style="display:flex;justify-content:space-between;align-items:center;font-size:14px;margin-bottom:3px;">
                        <span>
                            <?php self::render_flag_img($lang['flag'], 'sm', $lang['name']); ?>
                            <?php echo esc_html($lang['name']); ?>
                        </span>
                        <span style="color:#64748b;font-size:14px;"><?php echo esc_html($lang['published']); ?>/<?php echo esc_html($lang['total']); ?> published</span>
                    </div>
                    <?php self::render_lang_progress_bar($lang); ?>
                </div>
                <?php endforeach; ?>
            <?php endif; ?>
        <?php endif; ?>
        <div style="display:flex;align-items:center;gap:10px;margin-top:10px;font-size:14px;color:#94A3B8;">
            <span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#059669;margin-right:2px;"></span>published</span>
            <span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#EAB308;margin-right:2px;"></span>draft</span>
            <span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#E2E8F0;margin-right:2px;"></span>missing</span>
        </div>
        <div class="forge-suite-card-meta" style="margin-top:6px;">
            <?php echo esc_html($stats['lang_count']); ?> <?php esc_html_e('target languages', 'seoforge'); ?>
            &middot;
            <?php echo esc_html($stats['source_posts']); ?> <?php esc_html_e('source posts', 'seoforge'); ?>
        </div>
        <?php
    }

    public static function render_lang_progress_bar($lang) {
        $pct_green  = min(100, $lang['pct_published']);
        $pct_yellow = min(100 - $pct_green, $lang['pct_draft']);
        ?>
        <div style="height:6px;background:#E2E8F0;border-radius:3px;overflow:hidden;display:flex;">
            <?php if ($pct_green > 0): ?>
                <div style="width:<?php echo esc_attr($pct_green); ?>%;background:#059669;"></div>
            <?php endif; ?>
            <?php if ($pct_yellow > 0): ?>
                <div style="width:<?php echo esc_attr($pct_yellow); ?>%;background:#EAB308;"></div>
            <?php endif; ?>
        </div>
        <?php
    }

    public static function render_form_card_body($stats) {
        ?>
        <div class="forge-suite-big-number" style="color:#3B82F6;"><?php echo esc_html($stats['submissions_month']); ?></div>
        <div class="forge-suite-big-label"><?php esc_html_e('Submissions (30 days)', 'seoforge'); ?></div>
        <div class="forge-suite-pie-row">
            <span class="forge-suite-pill" style="background:#DBEAFE;color:#1D4ED8;"><?php echo esc_html($stats['conversion_rate']); ?>% <?php esc_html_e('conversion', 'seoforge'); ?></span>
            <span class="forge-suite-pill" style="background:#DBEAFE;color:#1D4ED8;"><?php echo esc_html($stats['active_forms']); ?> <?php esc_html_e('active forms', 'seoforge'); ?></span>
        </div>
        <div class="forge-suite-card-meta">
            <?php echo esc_html($stats['views_month']); ?> <?php esc_html_e('views', 'seoforge'); ?>
            &middot;
            <?php echo esc_html($stats['total_forms']); ?> <?php esc_html_e('total forms', 'seoforge'); ?>
        </div>
        <?php
    }

    public static function render_fieldforge_card_body($stats) {
        ?>
        <div class="forge-suite-big-number" style="color:#F43F5E;"><?php echo esc_html($stats['total_groups']); ?></div>
        <div class="forge-suite-big-label"><?php esc_html_e('Field Groups', 'seoforge'); ?></div>
        <div class="forge-suite-pie-row">
            <span class="forge-suite-pill" style="background:#FFF1F2;color:#E11D48;"><?php echo esc_html($stats['total_values']); ?> <?php esc_html_e('values', 'seoforge'); ?></span>
            <span class="forge-suite-pill" style="background:#FFF1F2;color:#E11D48;"><?php echo esc_html($stats['posts_with_fields']); ?> <?php esc_html_e('posts', 'seoforge'); ?></span>
        </div>
        <div class="forge-suite-card-meta">
            <?php echo esc_html($stats['total_values']); ?> <?php esc_html_e('field values stored', 'seoforge'); ?>
            &middot;
            <?php echo esc_html($stats['posts_with_fields']); ?> <?php esc_html_e('posts with fields', 'seoforge'); ?>
        </div>
        <?php
    }

    // ═════════════════════════════════════════════════════════════════
    // Utilities
    // ═════════════════════════════════════════════════════════════════

    /**
 * Render a language flag. originally the dashboard mixed sources
 * — the 15 on-disk SVG files for common languages, Unicode regional-
 * indicator emoji for the other 45+, a text badge when even the emoji
 * lookup failed. In the same row you'd get a French tricolour SVG next
 * to a 🇰🇿 emoji, with different aspect ratios, baselines, and
 * rendering quality. The tester flagged it as "looks like half the
 * assets failed to load".
 *
 * Fix: unify on the regional-indicator emoji path for every country
 * code. It covers every country uniformly, renders natively on every
 * modern OS in a consistent style, and doesn't depend on an asset set
 * we'd have to keep in sync with the language list. The old SVG files
 * stay on disk (we may reuse them elsewhere) but this widget no longer
 * consumes them.
 *
 * $flag is a 2-letter country code (e.g. 'fr', 'sa', 'kz', 'vn').
 * Size is 'sm' (14×10) or 'md' (20×14) to match the two use-sites.
 */
    public static function render_flag_img($flag, $size = 'sm', $alt = '') {
        $flag = strtolower((string) $flag);
        $w = $size === 'md' ? 20 : 14;
        $h = $size === 'md' ? 14 : 10;
        // font-size slightly below the box height so the emoji glyph sits
        // on the baseline the same way an SVG img would. line-height:1
        // pins the glyph vertically inside the span — otherwise different
        // emoji fonts render with different ascents and the row wobbles.
        $fs = $size === 'md' ? 18 : 13;
        $style = "display:inline-block;width:{$w}px;height:{$h}px;"
               . "line-height:{$h}px;font-size:{$fs}px;font-family:"
               . "\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Noto Color Emoji\",\"Twemoji Mozilla\",sans-serif;"
               . "text-align:center;vertical-align:middle;overflow:hidden;"
               . "border-radius:1px;";

        if (preg_match('/^[a-z]{2}$/', $flag)) {
            // 'a' → U+1F1E6, 'b' → U+1F1E7, … pair emits the country flag.
            $ri = function ($ch) {
                $cp = 0x1F1E6 + (ord($ch) - ord('a'));
                return mb_convert_encoding('&#' . $cp . ';', 'UTF-8', 'HTML-ENTITIES');
            };
            $emoji = $ri($flag[0]) . $ri($flag[1]);
            echo '<span aria-label="' . esc_attr($alt) . '" style="' . esc_attr($style) . '">' . esc_html($emoji) . '</span>';
            return;
        }

        // Non-standard code (shouldn't happen for our language list, but
        // kept as a safety net): show the code in a muted chip so the UI
        // doesn't break visually.
        $badge = 'display:inline-block;width:' . ($w + 4) . 'px;height:' . $h
               . 'px;line-height:' . $h . 'px;font-size:9px;font-weight:700;'
               . 'color:#64748b;background:#e2e8f0;border-radius:2px;'
               . 'text-align:center;vertical-align:middle;';
        echo '<span style="' . esc_attr($badge) . '">' . esc_html(strtoupper($flag)) . '</span>';
    }
}

} // end: if (!class_exists('Forge_Suite_Dashboard_Cards'))
