<?php
/**
 * OperatorDashboardHubPage — F201 first submenu with 2 tabs.
 *
 * Replaces the two separate menu items:
 *   - "SwapAds" (parent) — kept for back-compat, page redirects to hub
 *   - "Dashboard" submenu — promoted to FIRST submenu via menu_position
 *   - "Backlinks" submenu — added by BacklinksHubPage (F200)
 *
 * Tabs:
 *   - ?tab=balance (default): existing OperatorDashboardPage metric tiles
 *   - ?tab=setup: audience config + license + auto-inject toggle
 *
 * The Setup tab fetches /audience/definitions from the server to render
 * sub-niches / geo_buckets / traffic_ranges as checkbox groups. Operators
 * can enable/disable individual items rather than typing comma-separated
 * values.
 *
 * @since 1.5.2
 */
declare(strict_types=1);

namespace SwapAds\Client\Admin;

use SwapAds\Client\Http\Redirect;
use SwapAds\Client\Api\RestClient;
use SwapAds\Client\Audience\SiteAudience;
use SwapAds\Client\License\LicenseManager;
use SwapAds\Client\Http\Request;

final class OperatorDashboardHubPage
{
    public const MENU_SLUG = 'swapads-client-operator-hub';

    public const TAB_BALANCE = 'balance';
    public const TAB_SETUP   = 'setup';

    /**
     * Active tab (sanitized). Defaults to TAB_BALANCE.
     */
    public static function currentTab(): string
    {
        $t = Request::input('tab', self::TAB_BALANCE, 'get');
        return in_array($t, [self::TAB_BALANCE, self::TAB_SETUP], true) ? $t : self::TAB_BALANCE;
    }

    /**
     * Register WP hooks.
     *
     * @since 1.5.2
     */
    public static function register(): void
    {
        add_action('admin_menu', [self::class, 'addMenu']);
        // Back-compat: SettingsPage's old admin-post handler still works.
        add_action('admin_post_swapads_client_save', [SettingsPage::class, 'handleSave']);
        add_action('admin_post_swapads_client_save_audience', [self::class, 'handleSaveAudience']);
    }

    /**
     * Register the dashboard menu — FIRST submenu under SwapAds.
     *
     * Uses menu_position=0 (or 1) so it appears immediately after the
     * top-level "SwapAds" entry. Old SettingsPage / Dashboard submenus
     * are removed by the Plugin.php registration order.
     */
    public static function addMenu(): void
    {
        add_menu_page(
            'SwapAds Dashboard',
            'SwapAds',
            'manage_options',
            self::MENU_SLUG,
            [self::class, 'render'],
            'dashicons-randomize',
            81
        );
    }

    /**
     * Render the full hub page.
     *
     * @since 1.5.2
     */
    public static function render(): void
    {
        if (!current_user_can('manage_options')) {
            wp_die('Insufficient permissions', 'Forbidden', ['response' => 403]);
        }

        $tab = self::currentTab();
        echo '<div class="wrap swapads-page">';
        echo '<h1>' . esc_html__('SwapAds Dashboard', 'swapads-client') . '</h1>';
        echo self::renderTabs($tab);

        if ($tab === self::TAB_SETUP) {
            self::renderSetupTab();
        } else {
            OperatorDashboardPage::render();
        }

        echo '</div>'; // .wrap
    }

    /**
     * Render the tab nav (same shape as BacklinksHubPage so a11y attrs match).
     */
    public static function renderTabs(string $active): string
    {
        $balance = esc_url(add_query_arg('tab', self::TAB_BALANCE));
        $setup   = esc_url(add_query_arg('tab', self::TAB_SETUP));
        $h  = '<nav class="swapads-tab-nav" role="tablist" aria-label="Dashboard tabs">';
        $h .= '<a href="' . $balance . '" role="tab" aria-selected="' . ($active === self::TAB_BALANCE ? 'true' : 'false') . '"'
            . ' class="swapads-tab' . ($active === self::TAB_BALANCE ? ' swapads-tab--active' : '') . '">'
            . esc_html__('Balance', 'swapads-client') . '</a>';
        $h .= '<a href="' . $setup . '" role="tab" aria-selected="' . ($active === self::TAB_SETUP ? 'true' : 'false') . '"'
            . ' class="swapads-tab' . ($active === self::TAB_SETUP ? ' swapads-tab--active' : '') . '">'
            . esc_html__('Setup', 'swapads-client') . '</a>';
        $h .= '</nav>';
        return $h;
    }

    /**
     * Render the Setup tab: license panel + WP domain (read-only) +
     * auto-inject toggle + audience checkbox groups (sub-niches /
     * geo_buckets / traffic_ranges) fetched from /audience/definitions.
     *
     * Replaces the old SettingsPage form (which had a Server URL field
     * + default placement slug field — both removed 2026-07-30).
     *
     * @since 1.5.2
     */
    public static function renderSetupTab(): void
    {
        echo '<h2 style="margin-top:16px">' . esc_html__('Setup', 'swapads-client') . '</h2>';
        echo '<p class="description">'
            . esc_html__('Configure your license, auto-inject, and audience. Audience fields are fetched from the server — check the boxes that match your site.', 'swapads-client')
            . '</p>';

        // License status panel
        echo '<div class="swapads-card" style="margin-top:16px">';
        echo '<h3>' . esc_html__('License', 'swapads-client') . '</h3>';
        if (LicenseManager::isLicensed()) {
            echo '<p><strong>Status:</strong> <span class="swapads-status swapads-status--active">'
                . esc_html(LicenseManager::status()) . '</span></p>';
            echo '<p><strong>License key:</strong> <code>' . esc_html(LicenseManager::key()) . '</code></p>';
        } else {
            echo '<p>No active license. Activate via '
                . '<a href="' . esc_url(admin_url('admin.php?page=' . SettingsPage::MENU_SLUG)) . '">SwapAds Settings</a>.</p>';
        }
        // Plg-001 (2026-08-01): server-activation status + manual "Activate now" button.
        // Freemius opt-in is asynchronous; this button lets the operator force
        // a re-attempt without waiting for the next 12h cron tick.
        $fsStatus = \SwapAds\Client\License\FreemiusAutoActivator::getStatus();
        echo '<p><strong>Server activation:</strong> <span class="swapads-status swapads-status--' . esc_attr($fsStatus) . '">'
            . esc_html(ucfirst($fsStatus)) . '</span></p>';
        $last = \SwapAds\Client\License\FreemiusAutoActivator::getLastResult();
        if (is_array($last) && isset($last['error_code']) && $fsStatus !== 'active') {
            echo '<p class="description">Last error: <code>' . esc_html((string) $last['error_code']) . '</code> &mdash; '
                . esc_html((string) ($last['message'] ?? '')) . '</p>';
        }
        if ($fsStatus !== 'active') {
            $retryUrl = wp_nonce_url(
                add_query_arg(['action' => 'swapads_client_retry_activation'], admin_url('admin-post.php')),
                'swapads_client_retry_activation'
            );
            echo '<p><a class="button button-primary" href="' . esc_url($retryUrl) . '">'
                . esc_html__('Activate license now', 'swapads-client') . '</a></p>';
        }
        echo '<p><strong>Site domain:</strong> <code>' . esc_html(home_url()) . '</code></p>';
        echo '</div>';

        // Auto-inject toggle (banner injection into footer)
        $autoInject = (bool) get_option('swapads_client_auto_inject', false);
        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
        echo '<input type="hidden" name="action" value="swapads_client_save">';
        wp_nonce_field(SettingsPage::NONCE_ACTION);
        echo '<div class="swapads-card" style="margin-top:16px">';
        echo '<h3>' . esc_html__('Banner injection', 'swapads-client') . '</h3>';
        echo '<p><label><input type="checkbox" name="auto_inject" value="1" ' . checked($autoInject, true, false) . '> '
            . esc_html__('Auto-inject banner ads into site footer (requires shortcode or widget for placement).', 'swapads-client')
            . '</label></p>';
        echo '<p>' . get_submit_button('Save', 'primary', 'submit', false) . '</p>';
        echo '</div>';
        echo '</form>';

        // F235 (2026-07-31): Audience is now a TWO-STEP selector.
        // Step 1: pick a niche from server-defined dropdown (adult/igaming/cannabis).
        // Step 2: pick sub-niches scoped to that niche (filtered server-side).
        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
        echo '<input type="hidden" name="action" value="swapads_client_save_audience">';
        wp_nonce_field('swapads_client_save_audience');
        $current = SiteAudience::get();
        $definitions = self::fetchAudienceDefinitions();
        echo '<div class="swapads-card" style="margin-top:16px">';
        echo '<h3>' . esc_html__('Audience', 'swapads-client') . '</h3>';

        if ($definitions === null) {
            echo '<p><em>Could not fetch audience definitions from the server. Save the rest of your settings and try again once the server is reachable.</em></p>';
        } else {
            // Step 1: Niche dropdown (server-defined)
            self::renderNicheDropdown(
                (array) ($definitions['niches'] ?? []),
                (array) ($definitions['sub_niches_by_niche'] ?? []),
                (string) ($current['niche_code'] ?? ''),
                (array) ($current['sub_niches'] ?? [])
            );
            // Step 2 + 3: other dimensions (unchanged)
            self::renderAudienceCheckboxes('Geo buckets', 'geo_buckets', (array) ($definitions['geo_buckets'] ?? []), (array) ($current['geo_buckets'] ?? []));
            self::renderAudienceCheckboxes('Traffic ranges', 'traffic_ranges', (array) ($definitions['traffic_ranges'] ?? []), (array) ($current['traffic_ranges'] ?? []));
        }
        echo '<p>' . get_submit_button('Save audience', 'primary', 'submit', false) . '</p>';
        echo '</div>';
        echo '</form>';
    }

    /**
     * F235 (2026-07-31): Render the niche dropdown + sub-niche checkboxes.
     *
     * Architecture:
     * - Niche dropdown is single-select (Y1: operator picks 1 niche).
     *   Multiple niches deferred to Y1.5.
     * - Sub-niche checkboxes are scoped to the picked niche via JS:
     *   when operator changes niche, we show ONLY sub-niches for that niche.
     * - Sub-niche options are rendered server-side as <optgroup data-niche="X">
     *   OR <div data-niche="X"> so JS can show/hide without re-fetching.
     *
     * @param array<int, array{code: string, label: string}> $niches
     * @param array<string, array<int, array{code: string, label: string}>> $subNichesByNiche
     * @param string $currentNicheCode
     * @param array<int, string> $currentSubNiches
     */
    private static function renderNicheDropdown(array $niches, array $subNichesByNiche, string $currentNicheCode, array $currentSubNiches): void
    {
        echo '<fieldset style="margin: 12px 0; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px;">';
        echo '<legend style="font-weight: 600; padding: 0 6px;">' . esc_html__('Step 1: Pick your site niche', 'swapads-client') . '</legend>';
        echo '<p class="description">' . esc_html__('Your site is about ONE of these topics. Sub-niches in step 2 are scoped to your choice.', 'swapads-client') . '</p>';

        // Single-select dropdown
        echo '<select name="niche_code" id="swapads-niche-dropdown" style="min-width: 280px; padding: 6px 8px; font-size: 14px;">';
        echo '<option value="">— ' . esc_html__('Select a niche', 'swapads-client') . ' —</option>';
        foreach ($niches as $n) {
            $code = (string) $n['code'];
            $label = (string) $n['label'];
            $selected = selected($code, $currentNicheCode, false);
            echo '<option value="' . esc_attr($code) . '"' . $selected . '>'
                . esc_html($label) . ' (' . esc_html($code) . ')</option>';
        }
        echo '</select>';
        echo '</fieldset>';

        // Step 2: Sub-niche checkboxes scoped per niche (all rendered, JS shows the matching group)
        echo '<fieldset style="margin: 12px 0; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px;">';
        echo '<legend style="font-weight: 600; padding: 0 6px;">' . esc_html__('Step 2: Sub-niches that apply to your site', 'swapads-client') . '</legend>';
        echo '<p class="description">' . esc_html__('Check every category that fits. Multiple selections allowed within your niche.', 'swapads-client') . '</p>';
        echo '<div id="swapads-subniche-groups">';
        if (empty($subNichesByNiche)) {
            echo '<p><em>' . esc_html__('No sub-niche definitions available for any niche yet.', 'swapads-client') . '</em></p>';
        }
        foreach ($niches as $n) {
            $nicheCode = (string) $n['code'];
            $opts = (array) ($subNichesByNiche[$nicheCode] ?? []);
            if (empty($opts)) {
                continue;
            }
            $sel = array_flip($currentSubNiches);
            $isVisible = ($currentNicheCode !== '' && $currentNicheCode === $nicheCode);
            $displayStyle = $isVisible ? 'block' : 'none';
            echo '<div class="swapads-subniche-group" data-niche="' . esc_attr($nicheCode) . '" style="display: ' . $displayStyle . ';">';
            echo '<p><strong>' . esc_html((string) $n['label']) . '</strong></p>';
            echo '<div style="display: flex; flex-wrap: wrap; gap: 12px; max-width: 800px;">';
            foreach ($opts as $opt) {
                $code = (string) $opt['code'];
                $label = (string) $opt['label'];
                $checked = isset($sel[$code]);
                echo '<label style="display:inline-flex; align-items:center; gap:4px;">'
                    . '<input type="checkbox" name="sub_niches[]" value="' . esc_attr($code) . '"'
                    . ($checked ? ' checked' : '') . '>'
                    . esc_html($label) . ' <code style="font-size: 11px; color: #666;">' . esc_html($code) . '</code>'
                    . '</label>';
            }
            echo '</div>';
            echo '</div>';
        }
        echo '</div>'; // #swapads-subniche-groups
        echo '</fieldset>';

        // Inline JS for showing/hiding sub-niche groups based on niche selection
        echo '<script>(function(){'
            . 'var dropdown = document.getElementById("swapads-niche-dropdown");'
            . 'var groups = document.querySelectorAll(".swapads-subniche-group");'
            . 'function sync(){'
                . 'var v = dropdown.value;'
                . 'groups.forEach(function(g){'
                    . 'g.style.display = (g.dataset.niche === v) ? "block" : "none";'
                . '});'
            . '}'
            . 'if (dropdown) dropdown.addEventListener("change", sync);'
            . 'sync();'
            . '})();</script>';
    }

    /**
     * Render a single checkbox group for one audience dimension.
     *
     * @param string                 $title    Heading shown to the operator.
     * @param string                 $name     Form field name (also storage key).
     * @param array<int, string>     $options  Server-defined options (e.g. ['live','recorded']).
     * @param array<int, string>     $selected Currently selected option codes.
     */
    private static function renderAudienceCheckboxes(string $title, string $name, array $options, array $selected): void
    {
        if (empty($options)) {
            return;
        }
        $sel = array_flip($selected);
        echo '<fieldset style="margin: 12px 0; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px;">';
        echo '<legend style="font-weight: 600; padding: 0 6px;">' . esc_html__($title, 'swapads-client') . '</legend>';
        echo '<div style="display: flex; flex-wrap: wrap; gap: 12px; max-width: 800px;">';
        foreach ($options as $opt) {
            $optStr = (string) $opt;
            $checked = isset($sel[$optStr]);
            echo '<label style="display:inline-flex; align-items:center; gap:4px;">'
                . '<input type="checkbox" name="' . esc_attr($name) . '[]" value="' . esc_attr($optStr) . '"'
                . ($checked ? ' checked' : '') . '>'
                . '<code>' . esc_html($optStr) . '</code>'
                . '</label>';
        }
        echo '</div>';
        echo '</fieldset>';
    }

    /**
     * Fetch audience definitions from server via RestClient.
     *
     * @return array<string, mixed>|null Null on error or unlicensed.
     */
    public static function fetchAudienceDefinitions(): ?array
    {
        if (!LicenseManager::isLicensed()) {
            return null;
        }
        $client = RestClient::fromOption();
        $resp = $client->get('/v1/audience/definitions');
        if (!is_array($resp) || !isset($resp['data']) || !is_array($resp['data'])) {
            return null;
        }
        $data = $resp['data'];
        return [
            'niches'              => array_values((array) ($data['niches'] ?? [])),  // F235
            'sub_niches_by_niche' => array_map(
                fn ($arr) => array_values((array) $arr),
                (array) ($data['sub_niches_by_niche'] ?? [])
            ),  // F235
            'sub_niches'     => array_values((array) ($data['sub_niches'] ?? [])),
            'geo_buckets'    => array_values((array) ($data['geo_buckets'] ?? [])),
            'traffic_ranges' => array_values((array) ($data['traffic_ranges'] ?? [])),
        ];
    }

    /**
     * Handle the Save Audience POST.
     *
     * Reads checkbox groups from $_POST, calls SiteAudience::save(), and
     * redirects back to the hub Setup tab with a success/error flag.
     *
     * @since 1.5.2
     */
    public static function handleSaveAudience(): void
    {
        if (!current_user_can('manage_options')) {
            wp_die('Insufficient permissions', 'Forbidden', ['response' => 403]);
        }
        check_admin_referer('swapads_client_save_audience');
        // F235 (2026-07-31): niche_code is now single-select dropdown.
        // Empty string = no niche chosen (allowed; sub_niches cleared).
        // TODO-CLIENT-001 (2026-08-02 complete): use Request::input* so every $_POST
        // read is wp_unslash()'d (defense in depth for WP 4.x-6.4 where magic
        // quotes are on). Request::inputArray auto-handles missing-key + typeof check.
        $nicheCode     = Request::input('niche_code', '', 'post');
        $subNiches     = Request::inputArray('sub_niches', [], 'post');
        $geoBuckets    = Request::inputArray('geo_buckets', [], 'post');
        $trafficRanges = Request::inputArray('traffic_ranges', [], 'post');
        $result = SiteAudience::save($nicheCode, $subNiches, $geoBuckets, $trafficRanges);
        if (is_array($result) && ($result['success'] ?? false)) {
            Redirect::to(add_query_arg(['page' => self::MENU_SLUG, 'tab' => self::TAB_SETUP, 'saved' => 1], admin_url('admin.php')));
        } else {
            $err = is_array($result) && isset($result['error_code']) ? $result['error_code'] : 'UNKNOWN';
            Redirect::to(add_query_arg(['page' => self::MENU_SLUG, 'tab' => self::TAB_SETUP, 'error' => $err], admin_url('admin.php')));
        }
    }
}
