<?php
/**
 * Admin UI — top-level menu, tabbed pages, settings save handler.
 *
 * @package Brainwerk SEO Suite
 */

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

class Seoforge_Admin {

    const MENU_SLUG = 'brainwerk-seo-suite';
    const NONCE     = 'seoforge_save_settings';
    const CAP       = 'manage_options';

    public static function register( Seoforge_Loader $loader ) {
        $loader->add_action( 'admin_menu', __CLASS__, 'register_menu' );
        $loader->add_action( 'network_admin_menu', __CLASS__, 'register_network_menu' );
        $loader->add_action( 'admin_init', __CLASS__, 'maybe_save_settings' );
        $loader->add_action( 'admin_enqueue_scripts', __CLASS__, 'enqueue_assets' );
    }

    public static function tabs() {
        return array(
            'dashboard' => __( 'Dashboard', 'brainwerk-seo-suite' ),
            'visitors'  => __( 'Visitors', 'brainwerk-seo-suite' ),
            'sources'   => __( 'Sources', 'brainwerk-seo-suite' ),
            'pages'     => __( 'Pages', 'brainwerk-seo-suite' ),
            'seo'       => __( 'SEO Analyzer', 'brainwerk-seo-suite' ),
            'sitemap'   => __( 'Sitemap', 'brainwerk-seo-suite' ),
            'meta'      => __( 'Meta & Schema', 'brainwerk-seo-suite' ),
            'redirects' => __( 'Redirects', 'brainwerk-seo-suite' ),
            'not-found' => __( '404 Log', 'brainwerk-seo-suite' ),
            'tools'     => __( 'Tools', 'brainwerk-seo-suite' ),
            'import'    => __( 'Import', 'brainwerk-seo-suite' ),
            'ai'        => __( 'AI', 'brainwerk-seo-suite' ),
            'privacy'   => __( 'Privacy / GDPR', 'brainwerk-seo-suite' ),
            'audit'     => __( 'Audit Log', 'brainwerk-seo-suite' ),
            'license'   => __( 'License', 'brainwerk-seo-suite' ),
            'reports'   => __( 'Reports', 'brainwerk-seo-suite' ),
            'verify'    => __( 'Verify', 'brainwerk-seo-suite' ),
        );
    }

    /**
     * Group tabs into sections so the nav doesn't read as a flat 14-tab
     * wall. Mirrors the ShieldForge tab-grouping pattern. Order of groups
     * matters — they render left-to-right.
     */
    public static function tab_groups() {
        return array(
            'overview' => array(
                'label' => __( 'Overview', 'brainwerk-seo-suite' ),
                'tabs'  => array( 'dashboard' ),
            ),
            'insights' => array(
                'label' => __( 'Insights', 'brainwerk-seo-suite' ),
                'tabs'  => array( 'visitors', 'sources', 'pages' ),
            ),
            'seo' => array(
                'label' => __( 'SEO', 'brainwerk-seo-suite' ),
                'tabs'  => array( 'seo', 'sitemap', 'meta', 'ai' ),
            ),
            'issues' => array(
                'label' => __( 'Issues', 'brainwerk-seo-suite' ),
                'tabs'  => array( 'redirects', 'not-found' ),
            ),
            'system' => array(
                'label' => __( 'System', 'brainwerk-seo-suite' ),
                'tabs'  => array( 'tools', 'import', 'privacy', 'audit', 'license', 'reports', 'verify' ),
            ),
        );
    }

    public static function register_menu() {
        add_menu_page(
            __( 'Brainwerk SEO Suite', 'brainwerk-seo-suite' ),
            __( 'Brainwerk SEO Suite', 'brainwerk-seo-suite' ),
            self::CAP,
            self::MENU_SLUG,
            array( __CLASS__, 'render_page' ),
            SEOFORGE_URL . 'assets/images/icon-32.png',
            72
        );
    }

    public static function register_network_menu() {
        add_menu_page(
            __( 'Brainwerk SEO Suite', 'brainwerk-seo-suite' ),
            __( 'Brainwerk SEO Suite', 'brainwerk-seo-suite' ),
            'manage_network_options',
            self::MENU_SLUG,
            array( __CLASS__, 'render_page' ),
            SEOFORGE_URL . 'assets/images/icon-32.png',
            72
        );
    }

    public static function enqueue_assets( $hook ) {
        $is_seoforge_page = false !== strpos( (string) $hook, self::MENU_SLUG );
        $is_dashboard     = 'index.php' === $hook;
        // Also load on the post editor so our meta-box AI buttons have JS + nonce.
        $is_post_editor   = in_array( $hook, array( 'post.php', 'post-new.php' ), true );
        // ...and on the post/page list tables for the inline SEO-score column.
        $is_list_table    = 'edit.php' === $hook;
        if ( ! $is_seoforge_page && ! $is_dashboard && ! $is_post_editor && ! $is_list_table ) {
            return;
        }
        wp_enqueue_style(
            'seoforge-admin',
            SEOFORGE_URL . 'assets/css/admin.css',
            array(),
            SEOFORGE_VERSION
        );
        wp_enqueue_script(
            'seoforge-admin',
            SEOFORGE_URL . 'assets/js/admin.js',
            array( 'jquery', 'wp-api' ),
            SEOFORGE_VERSION,
            true
        );
        wp_localize_script(
            'seoforge-admin',
            'seoforgeAjax',
            array(
                'url'   => admin_url( 'admin-ajax.php' ),
                'nonce' => wp_create_nonce( Seoforge_Ajax::NONCE_ACTION ),
                'i18n'  => array(
                    'confirmDelete'   => __( 'Delete this entry?', 'brainwerk-seo-suite' ),
                    'confirmReset'    => __( 'Erase all stored visit data? This cannot be undone.', 'brainwerk-seo-suite' ),
                    'genericError'    => __( 'Request failed.', 'brainwerk-seo-suite' ),
                    'geoRegions'      => __( 'Top regions', 'brainwerk-seo-suite' ),
                    'geoCities'       => __( 'Top cities', 'brainwerk-seo-suite' ),
                    'geoNoDetail'     => __( 'No region/city data for this period yet — the city database populates within ~24h of enabling Pro geolocation.', 'brainwerk-seo-suite' ),
                    /* translators: %d = number analyzed, %r = number remaining */
                    'backfillProgress' => __( 'Analyzed %d, %r remaining…', 'brainwerk-seo-suite' ),
                    /* translators: %d = number of posts analyzed */
                    'backfillDone'    => __( 'Done — %d posts analyzed.', 'brainwerk-seo-suite' ),
                    'verifyRunning'   => __( 'Reconciling…', 'brainwerk-seo-suite' ),
                    /* translators: %d = number of log lines processed so far */
                    'verifyProgress'  => __( 'Processed %d log lines so far…', 'brainwerk-seo-suite' ),
                    'verifyDone'      => __( 'Done.', 'brainwerk-seo-suite' ),
                    'verifyCancelled' => __( 'Cancelled.', 'brainwerk-seo-suite' ),
                    'verifyBadRange'  => __( 'Please choose a start and end date/time.', 'brainwerk-seo-suite' ),
                    /* translators: %d = deviation count (log-side minus plugin-side) */
                    'verifyDeviation' => __( 'Deviation: %d', 'brainwerk-seo-suite' ),
                ),
            )
        );
    }

    public static function current_tab() {
        $tabs   = array_keys( self::tabs() );
        $hidden = array( 'onboarding' );
        $tab    = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'dashboard'; // phpcs:ignore WordPress.Security.NonceVerification
        if ( in_array( $tab, $tabs, true ) || in_array( $tab, $hidden, true ) ) {
            return $tab;
        }
        return 'dashboard';
    }

    public static function render_page() {
        if ( ! current_user_can( self::CAP ) && ! current_user_can( 'manage_network_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to access this page.', 'brainwerk-seo-suite' ) );
        }
        $tab = self::current_tab();
        ?>
        <div class="wrap seoforge-wrap">
            <h1 class="seoforge-title">
                <img src="<?php echo esc_url( SEOFORGE_URL . 'assets/images/icon-64.png' ); ?>" alt="Brainwerk SEO Suite" class="seoforge-brand-icon" width="36" height="36">
                <?php esc_html_e( 'Brainwerk SEO Suite', 'brainwerk-seo-suite' ); ?>
                <span class="seoforge-version">v<?php echo esc_html( SEOFORGE_VERSION ); ?></span>
            </h1>

            <nav class="nav-tab-wrapper seoforge-tabs seoforge-tabs-grouped">
                <?php
                $tabs = self::tabs();
                foreach ( self::tab_groups() as $group_key => $group ) :
                    if ( empty( $group['tabs'] ) ) { continue; } ?>
                    <div class="seoforge-tab-group" data-group="<?php echo esc_attr( $group_key ); ?>">
                        <span class="seoforge-tab-group-label"><?php echo esc_html( $group['label'] ); ?></span>
                        <div>
                            <?php foreach ( $group['tabs'] as $slug ) :
                                if ( ! isset( $tabs[ $slug ] ) ) { continue; } ?>
                                <a class="nav-tab <?php echo $slug === $tab ? 'nav-tab-active' : ''; ?>"
                                   href="<?php echo esc_url( self::tab_url( $slug ) ); ?>">
                                    <?php echo esc_html( $tabs[ $slug ] ); ?>
                                </a>
                            <?php endforeach; ?>
                        </div>
                    </div>
                <?php endforeach; ?>
            </nav>

            <?php settings_errors( 'brainwerk-seo-suite' ); ?>

            <div class="seoforge-tab-content">
                <?php
                $view = SEOFORGE_PATH . 'admin/views/page-' . $tab . '.php';
                if ( file_exists( $view ) ) {
                    include $view;
                } else {
                    self::render_placeholder( $tab );
                }
                ?>
            </div>
        </div>
        <?php
    }

    /**
     * Multisite scope selector for the analytics tabs (Dashboard / Visitors /
     * Sources / Pages). Lets the viewer switch between this site, the whole
     * network, or any single subsite. No-op on single-site installs and for
     * any user who isn't a super admin / manage_network_options holder — a
     * plain subsite Administrator only ever has manage_options for their own
     * site, so they never see the picker and Seoforge_Utils::stats_scope()
     * would force their choice back to 'current' anyway.
     */
    public static function scope_picker() {
        if ( ! is_multisite() || ! Seoforge_Utils::can_view_network_scope() ) {
            return;
        }
        $token = Seoforge_Utils::stats_scope_token();
        $tab   = self::current_tab();
        $sites = get_sites( array( 'number' => 200, 'fields' => 'ids' ) );
        // Preserve the range filter across a scope switch.
        $range = isset( $_GET['range'] ) ? max( 24, (int) $_GET['range'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        ?>
        <form method="get" class="seoforge-scope-picker">
            <input type="hidden" name="page" value="<?php echo esc_attr( self::MENU_SLUG ); ?>">
            <input type="hidden" name="tab" value="<?php echo esc_attr( $tab ); ?>">
            <?php if ( $range ) : ?>
                <input type="hidden" name="range" value="<?php echo esc_attr( $range ); ?>">
            <?php endif; ?>
            <label for="seoforge-site-select"><?php esc_html_e( 'Site:', 'brainwerk-seo-suite' ); ?></label>
            <select id="seoforge-site-select" name="seoforge_site" onchange="this.form.submit()">
                <option value="current" <?php selected( $token, 'current' ); ?>><?php esc_html_e( 'This site', 'brainwerk-seo-suite' ); ?></option>
                <option value="all" <?php selected( $token, 'all' ); ?>><?php esc_html_e( 'Entire network (all sites)', 'brainwerk-seo-suite' ); ?></option>
                <optgroup label="<?php esc_attr_e( 'Individual sites', 'brainwerk-seo-suite' ); ?>">
                    <?php foreach ( $sites as $sid ) :
                        $sid     = (int) $sid;
                        $details = get_blog_details( $sid );
                        $name    = ( $details && $details->blogname ) ? $details->blogname : ( '#' . $sid );
                        ?>
                        <option value="<?php echo esc_attr( $sid ); ?>" <?php selected( $token, (string) $sid ); ?>>
                            <?php echo esc_html( $name ); ?>
                        </option>
                    <?php endforeach; ?>
                </optgroup>
            </select>
            <noscript><button type="submit" class="button"><?php esc_html_e( 'Go', 'brainwerk-seo-suite' ); ?></button></noscript>
        </form>
        <?php
    }

    public static function tab_url( $slug ) {
        return add_query_arg(
            array( 'page' => self::MENU_SLUG, 'tab' => $slug ),
            is_network_admin() ? network_admin_url( 'admin.php' ) : admin_url( 'admin.php' )
        );
    }

    private static function render_placeholder( $tab ) {
        $labels = self::tabs();
        $label  = isset( $labels[ $tab ] ) ? $labels[ $tab ] : ucfirst( $tab );
        echo '<div class="seoforge-card"><h2>' . esc_html( $label ) . '</h2>';
        echo '<p>' . esc_html__( 'This section is coming soon.', 'brainwerk-seo-suite' ) . '</p></div>';
    }

    public static function maybe_save_settings() {
        if ( empty( $_POST['seoforge_action'] ) || 'save_settings' !== $_POST['seoforge_action'] ) { // phpcs:ignore WordPress.Security.NonceVerification
            return;
        }
        if ( ! check_admin_referer( self::NONCE ) ) {
            return;
        }
        if ( ! current_user_can( self::CAP ) && ! current_user_can( 'manage_network_options' ) ) {
            return;
        }
        // NOTE: the form fields are named seoforge[...] in every view, so the
        // POST bag is 'seoforge'. (A blanket slug rename had briefly changed this
        // key to 'brainwerk-seo-suite', which silently broke settings saving.)
        $input = isset( $_POST['seoforge'] ) && is_array( $_POST['seoforge'] )
            ? wp_unslash( $_POST['seoforge'] )
            : array();

        $boolean_keys = array(
            'tracking_enabled', 'tracking_exclude_admins', 'tracking_exclude_bots',
            'tracking_respect_dnt', 'privacy_anonymize_ip', 'privacy_hash_rotate_daily',
            'privacy_store_full_url', 'privacy_store_user_agent',
            'seo_meta_enabled', 'seo_force_takeover', 'seo_og_enabled', 'seo_twitter_enabled', 'seo_schema_enabled',
            'seo_breadcrumbs_enabled', 'seo_canonical_enabled', 'seo_remove_wp_emoji',
            'seo_clean_head', 'sitemap_enabled', 'robots_custom_enabled',
            'geo_llms_enabled', 'seo_schema_faq_enabled', 'notary_enabled',
            'redirects_enabled', 'log_404_enabled', 'auto_redirect_404_attached',
            'notify_email_enabled', 'weekly_digest_enabled', 'daily_digest_enabled', 'traffic_spike_alerts',
            'traffic_spike_rule_percent_enabled', 'traffic_spike_rule_absolute_enabled',
            'delete_on_uninstall',
        );
        $current_tab_keys = isset( $_POST['seoforge_tab_keys'] ) // phpcs:ignore WordPress.Security.NonceVerification
            ? array_map( 'sanitize_key', (array) wp_unslash( $_POST['seoforge_tab_keys'] ) )
            : array();
        foreach ( $current_tab_keys as $key ) {
            if ( in_array( $key, $boolean_keys, true ) && ! isset( $input[ $key ] ) ) {
                $input[ $key ] = 0;
            }
        }
        // Checkbox group: if the tab owns it but nothing is checked, clear it.
        if ( in_array( 'geo_blocked_ai_bots', $current_tab_keys, true ) && ! isset( $input['geo_blocked_ai_bots'] ) ) {
            $input['geo_blocked_ai_bots'] = array();
        }

        // The onboarding wizard only exposes one checkbox ("Send weekly digest
        // by email") for the master switch; mirror it onto weekly_digest_enabled
        // so opting in there actually turns the digest on (consent-first default
        // is false, see seed_defaults()).
        if ( isset( $_POST['seoforge_onboarding_done'] ) && array_key_exists( 'notify_email_enabled', $input ) ) { // phpcs:ignore WordPress.Security.NonceVerification
            $input['weekly_digest_enabled'] = $input['notify_email_enabled'];
        }

        $prev_delete_on_uninstall = Seoforge_Settings::get( 'delete_on_uninstall', null );

        // The back-fill loop above still submits every boolean the current tab
        // owns, not just the ones the user actually touched — but as of 1.9.0
        // Seoforge_Settings::update() itself only persists, per key, the ones
        // that differ from the network value, so a subsite's per-blog option
        // stays sparse and keeps inheriting future network-level changes to
        // everything it hasn't genuinely customized.
        Seoforge_Settings::update( $input );

        if ( class_exists( 'Seoforge_Audit' ) ) {
            Seoforge_Audit::log( 'settings_saved', array(
                'object_type' => 'settings',
                'object_id'   => self::current_tab(),
                'summary'     => sprintf( 'Saved %d setting(s) on the "%s" tab.', count( $input ), self::current_tab() ),
            ) );
            if ( array_key_exists( 'delete_on_uninstall', $input )
                    && (bool) $prev_delete_on_uninstall !== (bool) Seoforge_Settings::get( 'delete_on_uninstall', null ) ) {
                Seoforge_Audit::log( 'uninstall_decision_changed', array(
                    'object_type' => 'settings',
                    'object_id'   => 'delete_on_uninstall',
                    'summary'     => Seoforge_Settings::get( 'delete_on_uninstall', false )
                        ? 'Uninstall will now delete all plugin data.'
                        : 'Uninstall will now preserve all plugin data.',
                ) );
            }
        }

        if ( class_exists( 'Seoforge_Stats' ) ) {
            Seoforge_Stats::flush_cache();
        }
        if ( class_exists( 'Seoforge_Llms' ) ) {
            Seoforge_Llms::clear_cache();
        }

        // Mark onboarding done if user saves the wizard.
        if ( isset( $_POST['seoforge_onboarding_done'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
            Seoforge_Onboarding::complete();
        }

        // Reflush rewrites if sitemap settings changed.
        if ( isset( $input['sitemap_enabled'] ) ) {
            Seoforge_Sitemap::flush_rewrite_on_activate();
        }

        add_settings_error(
            'brainwerk-seo-suite',
            'seoforge_saved',
            __( 'Settings saved.', 'brainwerk-seo-suite' ),
            'updated'
        );
        wp_safe_redirect( self::tab_url( self::current_tab() ) );
        exit;
    }
}
