<?php
/**
 * Forge Suite Dashboard — Bundle License activation section.
 *
 * F13 phase 6 (2026-04-21 end-of-day). Final piece of the
 * `class-forge-dashboard.php` decomposition (2269 → ≤612 lines after
 * this extraction). Renders the "Bundle License" section on the Forge
 * Suite admin page: the status banner, the Connect-to-Avakode CTA,
 * manual license-key fallback, and per-plugin license-status pills.
 *
 * Pure static utility — no singleton, no hooks. Called from
 * `Forge_Suite_Dashboard::render` at the single site it's used.
 *
 * Shared across the 4 Forge plugins via `class_exists` guard. The
 * first-loaded copy wins.
 */
if (!defined('ABSPATH')) exit;

if (!class_exists('Forge_Suite_Dashboard_License_Activation')) {

class Forge_Suite_Dashboard_License_Activation {

    /**
 * Plugin → Freemius-bridge-function map. Each entry gives the pretty
 * name (for the pill), the accent color (for the status-dot), and the
 * global function the plugin's bootstrap defines that returns the
 * Freemius SDK instance. Matches the constant shape used in
 * Forge_Suite_Dashboard_Bundle_CTA::FS_FNS (F13 phase 5).
 */
    const PLUGINS = [
        'seo'        => ['name' => 'SEO Forge',   'color' => '#FF6B35', 'fn' => 'sf_freemius'],
        'lang'       => ['name' => 'Lang Forge',  'color' => '#0EC49E', 'fn' => 'lf_freemius'],
        'form'       => ['name' => 'Form Forge',  'color' => '#3B82F6', 'fn' => 'ff_freemius'],
        'fieldforge' => ['name' => 'Field Forge', 'color' => '#F43F5E', 'fn' => 'fdf_freemius'],
    ];

    /**
 * Admin-page slug map used by the "Not connected" pill's future link
 * surface. Kept here (not on the facade) so the view is self-contained.
 */
    const ADMIN_SLUGS = [
        'seo'        => 'seoforge',
        'lang'       => 'lf-settings',
        'form'       => 'formforge',
        'fieldforge' => 'fieldforge',
    ];

    /**
 * Render the Bundle License section. Echoes HTML to the current
 * output buffer — identical contract to the previous
 * Forge_Suite_Dashboard::render_license_activation method it
 * replaces.
 */
    public static function render() {
        $plugins_info = self::PLUGINS;

        // Probe each plugin's Freemius state once, up front. Used by
        // (a) the PRO-count banner at the top of the section and
        // (b) the per-plugin pill loop at the bottom. Doing it once
        // avoids 4× repeat calls to Freemius's cached introspection.
        $any_pro = false;
        $pro_count = 0;
        foreach ($plugins_info as $key => $info) {
            if (function_exists($info['fn'])) {
                $fs = call_user_func($info['fn']);
                if ($fs->is_paying() || $fs->is_trial() || self::local_plugin_is_pro($key)) {
                    $any_pro = true;
                    $pro_count++;
                }
            }
        }
        $all_pro = ($pro_count >= 4);

        // FC-63 v2: the site is "connected" when it is bound to an Avakode
        // account (binding present) OR any plugin currently resolves to PRO.
        // Connect / Reconnect / Disconnect is driven by this — NOT by manual key
        // entry, which is removed entirely (Sergei FC-63 v2).
        $binding_exists = class_exists('Forge_Connect')
            && method_exists('Forge_Connect', 'account_binding')
            && !empty(Forge_Connect::account_binding()['account_hash']);
        $connected = $binding_exists || $any_pro;

        // Representative product for the shared Connect button. Connect fans the
        // account's full entitlements out to every plugin on return, so any
        // installed Forge plugin works as the entry point.
        $connect_product = null;
        foreach (['lang' => 'langforge', 'seo' => 'seoforge', 'form' => 'formforge', 'fieldforge' => 'fieldforge'] as $k => $slug) {
            if (function_exists($plugins_info[$k]['fn'])) { $connect_product = $slug; break; }
        }
        ?>
        <div class="forge-suite-section forge-suite-license-section">

            <h2 class="forge-suite-section-title">
                <span class="dashicons dashicons-admin-network" style="color:#7C5CFC;"></span>
                <?php esc_html_e('Bundle License', 'seoforge'); ?>
            </h2>

            <?php self::render_connect_result(); ?>

            <?php if ($connected): ?>
            <!-- Connected status banner + full-site Disconnect -->
            <div class="forge-suite-license-active-banner">
                <div class="forge-suite-license-active-info">
                    <span class="dashicons dashicons-yes-alt" style="color:#059669;font-size:20px;width:20px;height:20px;"></span>
                    <div>
                        <strong><?php echo $any_pro
                            /* translators: %d: number of Forge Suite plugins (out of 4) that have PRO active. */
                            ? sprintf(esc_html__('PRO active on %d of 4 plugins', 'seoforge'), $pro_count)
                            : esc_html__('Connected to Avakode', 'seoforge'); ?></strong>
                        <span class="forge-suite-license-active-sub"><?php echo $all_pro
                            ? esc_html__('All premium features unlocked.', 'seoforge')
                            : esc_html__('Connected to your Avakode account.', 'seoforge'); ?></span>
                    </div>
                </div>
                <button type="button" id="forge-suite-deactivate-btn" class="forge-suite-deactivate-btn">
                    <span class="dashicons dashicons-dismiss"></span>
                    <?php esc_html_e('Disconnect', 'seoforge'); ?>
                </button>
            </div>
            <?php endif; ?>

            <p class="forge-suite-license-desc">
                <?php echo $connected
                    ? esc_html__('Reconnect to re-pull licenses and credits from your Avakode account after a purchase or plan change.', 'seoforge')
                    : esc_html__('Connect this site to your Avakode account to activate PRO on every Forge plugin you own — bundle or single. One approve covers everything.', 'seoforge'); ?>
            </p>
            <?php
            // Single Connect / Reconnect entry point — no manual key entry (FC-63 v2).
            if ($connect_product && class_exists('Forge_Connect')) {
                Forge_Connect::render_button($connect_product, '#7C5CFC', 'suite');
            }
            ?>

            <div id="forge-suite-license-result" class="forge-suite-license-result" style="display:none;"></div>

            <!-- Per-plugin license status -->
            <?php self::render_status_pills($plugins_info); ?>

        </div>
        <?php
        // JS lives in assets/js/forge-dashboard.js (F12 phase 2).
        // Config (plugins array, deactNonce, i18n) is injected via
        // wp_localize_script from enqueue_assets.
    }

    /**
 * FC-63 v2: render the Connect return banner (success / account-conflict) on
 * the Forge Suite dashboard. Connect/Reconnect can now be initiated from the
 * Suite, so its result (incl. the one-site-one-account block) must surface
 * here too. Reads the same query args the per-plugin License pages render;
 * the shared cleanup script strips them after first paint.
 */
    private static function render_connect_result(): void {
        if (!empty($_GET['forge_connect_success'])) {
            echo '<div class="notice notice-success" style="margin:12px 0;"><p>'
                . esc_html__('Connected to Avakode successfully. Your licenses and credits are now in sync.', 'seoforge')
                . '</p></div>';
        }
        if (!empty($_GET['forge_connect_error'])) {
            echo '<div class="notice notice-error" style="margin:12px 0;"><p>'
                . esc_html(wp_unslash($_GET['forge_connect_error']))
                . '</p></div>';
        }
    }

    /**
 * Per-plugin status pills at the bottom of the section. Extracted from
 * render for readability — the loop is meaty enough to warrant its
 * own helper. Takes the same $plugins_info map so the two renders
 * stay in lockstep if the list grows.
 */
    private static function render_status_pills(array $plugins_info): void {
        // v2 (2026-05-03): Sergei reported mixed labels after a bundle
        // deactivation — 3 plugins read "Free" while one read "Not connected"
        // because Tier 3 force-wipe nuked Freemius local state for the latter
        // but Tier 1 polite deactivate left it intact for the others. The
        // underlying difference is internal plumbing the user shouldn't see.
        
        // Compute a single "user has connected to Forge Suite" flag — true if
        // ANY of the 4 plugins is currently Freemius-registered, OR if the
        // user has an active Forge token (SSO connection to avakode.com), OR
        // if we've recorded a forge_user_ever_connected marker. If true, all
        // non-PRO plugins render as "Free". If false, they render as "Not
        // connected". No more mixed states.
        $user_ever_connected = (bool) get_option('forge_user_ever_connected', false);
        if (!$user_ever_connected) {
            foreach ($plugins_info as $info) {
                if (function_exists($info['fn'])) {
                    $fs = call_user_func($info['fn']);
                    if ($fs->is_registered()) {
                        $user_ever_connected = true;
                        // Persist so a later force-wipe doesn't flip the UI
                        // back to "Not connected" — the user has connected.
                        update_option('forge_user_ever_connected', true, false);
                        break;
                    }
                }
            }
        }
        ?>
        <div class="forge-suite-license-status">
            <?php
            foreach ($plugins_info as $key => $info) {
                $is_pro = false;
                $is_registered = false;
                $plan_label = 'N/A';

                if (function_exists($info['fn'])) {
                    $fs = call_user_func($info['fn']);
                    $is_registered = $fs->is_registered();
                    if ($fs->is_paying() || self::local_plugin_is_pro($key)) {
                        $is_pro = true;
                        // FC-63 v2: UI no longer splits PRO by source (bundle vs
                        // single). entitlement_source stays inside the resolver
                        // for debug, but the user just sees "PRO".
                        $plan_label = 'PRO';
                    } elseif ($fs->is_trial()) {
                        $is_pro = true;
                        $plan_label = 'Trial';
                    } elseif ($user_ever_connected) {
                        // User has connected (now or previously) — show "Free"
                        // uniformly across all non-PRO plugins, even ones whose
                        // local Freemius state was force-wiped during a bundle
                        // deactivation tier-3 fallback.
                        $plan_label = 'Free';
                    } else {
                        $plan_label = __('Not connected', 'seoforge');
                    }
                } else {
                    $plan_label = __('Not active', 'seoforge');
                }

                $dot_color = $is_pro
                    ? $info['color']
                    : ($user_ever_connected ? '#CBD5E1' : '#F59E0B');
                $plan_class = $is_pro
                    ? 'forge-suite-license-plan--pro'
                    : (!$user_ever_connected && function_exists($info['fn']) ? 'forge-suite-license-plan--warn' : '');
                ?>
                <div class="forge-suite-license-pill">
                    <span class="forge-suite-license-dot" style="background:<?php echo esc_attr($dot_color); ?>;"></span>
                    <span class="forge-suite-license-name"><?php echo esc_html($info['name']); ?></span>
                    <span class="forge-suite-license-plan <?php echo esc_attr($plan_class); ?>"><?php echo esc_html($plan_label); ?></span>
                </div>
                <?php
            }
            ?>
        </div>
        <?php
    }

    private static function local_plugin_is_pro(string $key): bool {
        if (self::plugin_license_manager_is_pro($key)) {
            return true;
        }

        if (!defined('FORGE_SKIP_LICENSE_REVALIDATION') || !FORGE_SKIP_LICENSE_REVALIDATION) {
            return false;
        }
        if (!in_array(get_option('forge_e2e_plan', ''), ['pro', 'trial'], true)) {
            return false;
        }

        $option_map = [
            'seo'        => 'seoforge_license_data',
            'lang'       => 'lf_license_data',
            'form'       => 'formforge_license_data',
            'fieldforge' => 'fieldforge_license_data',
        ];
        if (empty($option_map[$key])) {
            return false;
        }
        $data = get_option($option_map[$key], []);
        return is_array($data) && !empty($data['key']);
    }

    private static function plugin_license_manager_is_pro(string $key): bool {
        switch ($key) {
            case 'seo':
                return class_exists('SEOFORGE_License') && SEOFORGE_License::is_pro();
            case 'lang':
                return class_exists('LF_License') && LF_License::is_pro();
            case 'form':
                return class_exists('FORMFORGE_License') && FORMFORGE_License::instance()->is_pro();
            case 'fieldforge':
                return class_exists('FIELDFORGE_License') && FIELDFORGE_License::instance()->is_pro();
        }
        return false;
    }
}

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