<?php
/**
 * Verify — the data-verification suite: hash-chain integrity, gap detection,
 * the determinism self-check, and nginx access-log reconciliation.
 *
 * @package Brainwerk SEO Suite
 */
if ( ! defined( 'ABSPATH' ) ) { exit; }
if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'manage_network_options' ) ) return;

$can_network = is_multisite() && Seoforge_Utils::can_view_network_scope();
$current_blog_id = Seoforge_Utils::current_blog_id();

$target_blog_id = $current_blog_id;
if ( $can_network && isset( $_GET['verify_blog'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $requested = (int) $_GET['verify_blog']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    if ( get_site( $requested ) ) {
        $target_blog_id = $requested;
    }
}

$state          = Seoforge_Verify::get_ui_state( $target_blog_id );
$coverage_since = Seoforge_History_Chain::covered_since( $target_blog_id );
$notary_status  = Seoforge_Notary::status( $target_blog_id );
$ran            = isset( $_GET['seoforge_verify_ran'] ) ? sanitize_key( wp_unslash( $_GET['seoforge_verify_ran'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

$ran_messages = array(
    'backfilled'          => array( 'success', __( 'Day backfilled and the gap list refreshed.', 'brainwerk-seo-suite' ) ),
    'backfill_refused'    => array( 'error', __( 'That day could not be backfilled — its raw data has already aged out of retention.', 'brainwerk-seo-suite' ) ),
    'backfill_bad_date'   => array( 'error', __( 'Invalid date.', 'brainwerk-seo-suite' ) ),
    'uploaded'            => array( 'success', __( 'Log file uploaded. You can now reconcile against it below.', 'brainwerk-seo-suite' ) ),
    'upload_failed'       => array( 'error', __( 'Upload failed.', 'brainwerk-seo-suite' ) ),
    'upload_bad_type'     => array( 'error', __( 'Only .log and .gz files are accepted.', 'brainwerk-seo-suite' ) ),
    'notary_pushed'       => array( 'success', __( 'Checkpoint pushed to the notarization hub.', 'brainwerk-seo-suite' ) ),
    'notary_push_failed'  => array( 'error', __( 'The checkpoint push failed — see the error below.', 'brainwerk-seo-suite' ) ),
    'notary_compared'     => array( 'success', __( 'Comparison against the hub finished — see the result below.', 'brainwerk-seo-suite' ) ),
);
if ( isset( $ran_messages[ $ran ] ) ) {
    printf(
        '<div class="notice notice-%s is-dismissible"><p>%s</p></div>',
        esc_attr( $ran_messages[ $ran ][0] ),
        esc_html( $ran_messages[ $ran ][1] )
    );
}
?>

<?php if ( $can_network ) : ?>
<form method="get" class="seoforge-scope-picker" style="margin-bottom:14px;">
    <input type="hidden" name="page" value="<?php echo esc_attr( Seoforge_Admin::MENU_SLUG ); ?>">
    <input type="hidden" name="tab" value="verify">
    <label for="seoforge-verify-blog-select"><?php esc_html_e( 'Site:', 'brainwerk-seo-suite' ); ?></label>
    <select id="seoforge-verify-blog-select" name="verify_blog" onchange="this.form.submit()">
        <?php foreach ( get_sites( array( 'number' => 200, 'fields' => 'ids' ) ) 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( $target_blog_id, $sid ); ?>><?php echo esc_html( $name ); ?></option>
        <?php endforeach; ?>
    </select>
    <noscript><button type="submit" class="button"><?php esc_html_e( 'Go', 'brainwerk-seo-suite' ); ?></button></noscript>
    <span class="description"><?php esc_html_e( '— gaps, chain integrity and the determinism check can be run for any site; log reconciliation always runs for the site you are currently on.', 'brainwerk-seo-suite' ); ?></span>
</form>
<?php endif; ?>

<!-- ============================================================ Chain ==== -->
<div class="seoforge-card">
    <h2><?php esc_html_e( 'Integrity chain', 'brainwerk-seo-suite' ); ?></h2>
    <p class="description">
        <?php esc_html_e( 'Every history row captured since upgrading to 1.9.1 is chained to the one before it with a SHA-256 hash, so any later tampering with a stored row — or a deleted/reordered row — is detectable.', 'brainwerk-seo-suite' ); ?>
    </p>
    <?php if ( $coverage_since ) : ?>
        <p><strong><?php printf(
            /* translators: %s: date the chain starts covering this site's history from */
            esc_html__( 'Covered by the integrity chain since %s.', 'brainwerk-seo-suite' ),
            esc_html( $coverage_since )
        ); ?></strong><br>
        <span class="description"><?php esc_html_e( 'Rows captured before that date predate this feature and are NOT covered — their presence is not cryptographically proven, only their existence in the database.', 'brainwerk-seo-suite' ); ?></span></p>
    <?php else : ?>
        <p><em><?php esc_html_e( 'No chained rows yet for this site — the chain starts with the next daily capture.', 'brainwerk-seo-suite' ); ?></em></p>
    <?php endif; ?>

    <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
        <?php wp_nonce_field( Seoforge_Verify::NONCE ); ?>
        <input type="hidden" name="action" value="seoforge_verify_run_chain">
        <input type="hidden" name="target_blog_id" value="<?php echo esc_attr( $target_blog_id ); ?>">
        <button type="submit" class="button button-primary"><?php esc_html_e( 'Run chain verify', 'brainwerk-seo-suite' ); ?></button>
    </form>

    <?php if ( ! empty( $state['chain'] ) ) :
        $chain = $state['chain'];
        ?>
        <div style="margin-top:14px;">
            <?php if ( ! empty( $chain['ok'] ) ) : ?>
                <p style="color:#16a34a;">✓ <?php
                    printf(
                        /* translators: 1: number of chained rows checked, 2: first covered date, 3: last covered date */
                        esc_html__( 'Chain intact — %1$d chained row(s) verified, covering %2$s through %3$s.', 'brainwerk-seo-suite' ),
                        (int) $chain['checked'],
                        esc_html( (string) $chain['covered_from'] ),
                        esc_html( (string) $chain['covered_to'] )
                    );
                ?></p>
            <?php elseif ( empty( $chain['checked'] ) ) : ?>
                <p><em><?php esc_html_e( 'Nothing to verify yet — no chained rows for this site.', 'brainwerk-seo-suite' ); ?></em></p>
            <?php else : ?>
                <p style="color:#dc2626;">✗ <?php
                    printf(
                        /* translators: 1: date the break was found at, 2: break type (content or link) */
                        esc_html__( 'Break detected at %1$s (%2$s mismatch).', 'brainwerk-seo-suite' ),
                        esc_html( (string) $chain['break']['date'] ),
                        esc_html( (string) $chain['break']['type'] )
                    );
                ?></p>
                <table class="widefat striped" style="max-width:720px;">
                    <tbody>
                        <tr><th style="width:160px;"><?php esc_html_e( 'Expected', 'brainwerk-seo-suite' ); ?></th><td><code><?php echo esc_html( (string) $chain['break']['expected'] ); ?></code></td></tr>
                        <tr><th><?php esc_html_e( 'Actual', 'brainwerk-seo-suite' ); ?></th><td><code><?php echo esc_html( (string) $chain['break']['actual'] ); ?></code></td></tr>
                    </tbody>
                </table>
            <?php endif; ?>
        </div>
    <?php endif; ?>
</div>

<!-- ==================================================== Notarization ==== -->
<div class="seoforge-card">
    <h2><?php esc_html_e( 'External notarization', 'brainwerk-seo-suite' ); ?></h2>
    <p class="description">
        <?php esc_html_e( 'Sends only the chain head (a SHA-256 hash), the date range it covers and a row count — never a measurement — to the ShieldForge-Intel hub, once a day. This is a separate opt-in from the SEO benchmark network: you can notarize without ever sending a benchmark snapshot, and the other way around.', 'brainwerk-seo-suite' ); ?>
    </p>
    <p class="description">
        <?php
        printf(
            /* translators: %s: link to the Privacy / GDPR tab */
            esc_html__( 'If this site isn\'t registered with the hub yet, turning this on for the first time performs that registration automatically, as a one-time step — it transmits only this site\'s web address, nothing else. See %s for how that registration is shared with the SEO benchmark network.', 'brainwerk-seo-suite' ),
            '<a href="' . esc_url( admin_url( 'admin.php?page=' . Seoforge_Admin::MENU_SLUG . '&tab=privacy' ) ) . '">' . esc_html__( 'Privacy / GDPR', 'brainwerk-seo-suite' ) . '</a>'
        );
        ?>
    </p>
    <p class="description" style="font-weight:600;">
        <?php esc_html_e( 'What this does and does not prove:', 'brainwerk-seo-suite' ); ?>
    </p>
    <ul class="description" style="margin-top:0;list-style:disc;padding-left:20px;">
        <li><?php esc_html_e( 'It protects against retroactive tampering only from the first accepted checkpoint onward. History from before that first checkpoint is not protected — there is nothing external to compare it against.', 'brainwerk-seo-suite' ); ?></li>
        <li><?php esc_html_e( 'It proves the chain was not altered after notarization, not that its content was correct to begin with — a row that was already wrong or fabricated the day it was captured notarizes exactly as cleanly as a correct one.', 'brainwerk-seo-suite' ); ?></li>
        <li><?php esc_html_e( 'The hub is a trusted third party. This is not a zero-trust proof; it moves "trust the site operator alone" to "trust the site operator and an independent, harder-to-quietly-rewrite external record".', 'brainwerk-seo-suite' ); ?></li>
    </ul>

    <form method="post" action="<?php echo esc_url( admin_url( 'admin.php?page=' . Seoforge_Admin::MENU_SLUG . '&tab=verify' ) ); ?>">
        <?php wp_nonce_field( Seoforge_Admin::NONCE ); ?>
        <input type="hidden" name="seoforge_action" value="save_settings">
        <input type="hidden" name="seoforge_tab_keys[]" value="notary_enabled">
        <label>
            <input type="checkbox" name="seoforge[notary_enabled]" value="1" <?php checked( $notary_status['enabled'], true ); ?>>
            <?php esc_html_e( 'Notarize this site\'s integrity chain daily', 'brainwerk-seo-suite' ); ?>
        </label>
        <?php submit_button( __( 'Save notarization setting', 'brainwerk-seo-suite' ), 'secondary', 'submit', false ); ?>
    </form>

    <table class="form-table" style="margin-top:14px;">
        <tbody>
            <tr>
                <th style="width:220px;"><?php esc_html_e( 'Status', 'brainwerk-seo-suite' ); ?></th>
                <td>
                    <?php if ( ! $notary_status['enabled'] ) : ?>
                        <em><?php esc_html_e( 'Not opted in.', 'brainwerk-seo-suite' ); ?></em>
                    <?php elseif ( ! $notary_status['registered'] ) : ?>
                        <span style="color:#dc2626;"><?php esc_html_e( 'Opted in, but not yet registered with the hub — this resolves itself on the next daily run, or click "Push checkpoint now" below.', 'brainwerk-seo-suite' ); ?></span>
                    <?php else : ?>
                        <span style="color:#16a34a;"><?php esc_html_e( 'Active.', 'brainwerk-seo-suite' ); ?></span>
                    <?php endif; ?>
                </td>
            </tr>
            <tr>
                <th><?php esc_html_e( 'Last checkpoint pushed', 'brainwerk-seo-suite' ); ?></th>
                <td>
                    <?php if ( $notary_status['last_pushed_at'] ) : ?>
                        <?php echo esc_html( $notary_status['last_pushed_at'] ); ?> UTC
                        <br><code style="font-size:11px;"><?php echo esc_html( $notary_status['last_checkpoint_id'] ); ?></code>
                    <?php else : ?>
                        &mdash;
                    <?php endif; ?>
                </td>
            </tr>
            <?php if ( ! empty( $notary_status['last_push_error'] ) ) : ?>
            <tr>
                <th><?php esc_html_e( 'Last push error', 'brainwerk-seo-suite' ); ?></th>
                <td style="color:#b32d2e;">
                    <?php echo esc_html( $notary_status['last_push_error']['code'] ); ?>
                    (<?php echo esc_html( $notary_status['last_push_error']['at'] ); ?> UTC)
                </td>
            </tr>
            <?php endif; ?>
            <?php if ( ! empty( $notary_status['last_compare'] ) ) :
                $cmp = $notary_status['last_compare'];
                ?>
            <tr>
                <th><?php esc_html_e( 'Last comparison with the hub', 'brainwerk-seo-suite' ); ?></th>
                <td>
                    <?php if ( 'match' === $cmp['status'] ) : ?>
                        <span style="color:#16a34a;">✓ <?php esc_html_e( 'Matches the hub\'s record.', 'brainwerk-seo-suite' ); ?></span>
                    <?php elseif ( 'mismatch' === $cmp['status'] ) : ?>
                        <span style="color:#dc2626;">✗ <?php
                            if ( ! empty( $cmp['since'] ) ) {
                                printf(
                                    /* translators: %s: the date the deviation starts from */
                                    esc_html__( 'Deviates from the hub\'s record, since at least %s.', 'brainwerk-seo-suite' ),
                                    esc_html( (string) $cmp['since'] )
                                );
                            } else {
                                esc_html_e( 'Deviates from the hub\'s record — no local chain data to pin a start date to.', 'brainwerk-seo-suite' );
                            }
                        ?></span>
                    <?php elseif ( 'no_checkpoint' === $cmp['status'] ) : ?>
                        <em><?php esc_html_e( 'The hub has no checkpoint on record yet for this site.', 'brainwerk-seo-suite' ); ?></em>
                    <?php else : ?>
                        <span style="color:#b32d2e;"><?php echo esc_html( isset( $cmp['message'] ) ? (string) $cmp['message'] : 'error' ); ?></span>
                    <?php endif; ?>
                    <br><span class="description"><?php echo esc_html( $cmp['at'] ); ?> UTC</span>
                </td>
            </tr>
            <?php endif; ?>
        </tbody>
    </table>

    <p style="display:flex;gap:10px;">
        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
            <?php wp_nonce_field( Seoforge_Notary::NONCE ); ?>
            <input type="hidden" name="action" value="seoforge_notary_push_now">
            <input type="hidden" name="target_blog_id" value="<?php echo esc_attr( $target_blog_id ); ?>">
            <button type="submit" class="button" <?php disabled( $notary_status['enabled'], false ); ?>><?php esc_html_e( 'Push checkpoint now', 'brainwerk-seo-suite' ); ?></button>
        </form>
        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
            <?php wp_nonce_field( Seoforge_Notary::NONCE ); ?>
            <input type="hidden" name="action" value="seoforge_notary_compare_now">
            <input type="hidden" name="target_blog_id" value="<?php echo esc_attr( $target_blog_id ); ?>">
            <button type="submit" class="button"><?php esc_html_e( 'Compare with hub', 'brainwerk-seo-suite' ); ?></button>
        </form>
    </p>
</div>

<!-- ============================================================ Gaps ==== -->
<div class="seoforge-card">
    <h2><?php esc_html_e( 'Gap detection', 'brainwerk-seo-suite' ); ?></h2>
    <p class="description"><?php esc_html_e( 'Finds calendar dates with no captured history row in the trailing window. A day still inside the visit-retention window can be backfilled from raw data; an older day cannot, because the raw data it would be computed from no longer exists.', 'brainwerk-seo-suite' ); ?></p>

    <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:flex;align-items:flex-end;gap:14px;flex-wrap:wrap;">
        <?php wp_nonce_field( Seoforge_Verify::NONCE ); ?>
        <input type="hidden" name="action" value="seoforge_verify_run_gaps">
        <input type="hidden" name="target_blog_id" value="<?php echo esc_attr( $target_blog_id ); ?>">
        <div>
            <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;"><?php esc_html_e( 'Trailing days', 'brainwerk-seo-suite' ); ?></label>
            <input type="number" name="days" min="1" max="1095" value="<?php echo esc_attr( isset( $state['gaps_days'] ) ? (int) $state['gaps_days'] : Seoforge_Verify::CRON_GAP_WINDOW_DAYS ); ?>" style="width:100px;">
        </div>
        <button type="submit" class="button button-primary"><?php esc_html_e( 'Run gap check', 'brainwerk-seo-suite' ); ?></button>
    </form>

    <?php if ( isset( $state['gaps'] ) ) :
        $gaps = $state['gaps'];
        ?>
        <div style="margin-top:14px;">
            <?php if ( empty( $gaps ) ) : ?>
                <p style="color:#16a34a;">✓ <?php
                    printf(
                        /* translators: %d: number of trailing days checked */
                        esc_html__( 'No gaps in the trailing %d days.', 'brainwerk-seo-suite' ),
                        (int) $state['gaps_days']
                    );
                ?></p>
            <?php else : ?>
                <table class="widefat striped" style="max-width:640px;">
                    <thead><tr>
                        <th><?php esc_html_e( 'Missing date', 'brainwerk-seo-suite' ); ?></th>
                        <th><?php esc_html_e( 'Status', 'brainwerk-seo-suite' ); ?></th>
                        <th><?php esc_html_e( 'Action', 'brainwerk-seo-suite' ); ?></th>
                    </tr></thead>
                    <tbody>
                    <?php foreach ( $gaps as $gap ) : ?>
                        <tr>
                            <td><code><?php echo esc_html( $gap['date'] ); ?></code></td>
                            <td>
                                <?php if ( $gap['backfillable'] ) : ?>
                                    <?php esc_html_e( 'Backfillable', 'brainwerk-seo-suite' ); ?>
                                <?php else : ?>
                                    <?php esc_html_e( 'Raw data aged out — cannot be backfilled', 'brainwerk-seo-suite' ); ?>
                                <?php endif; ?>
                            </td>
                            <td>
                                <?php if ( $gap['backfillable'] ) : ?>
                                    <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
                                        <?php wp_nonce_field( Seoforge_Verify::NONCE ); ?>
                                        <input type="hidden" name="action" value="seoforge_verify_backfill_gap">
                                        <input type="hidden" name="target_blog_id" value="<?php echo esc_attr( $target_blog_id ); ?>">
                                        <input type="hidden" name="date" value="<?php echo esc_attr( $gap['date'] ); ?>">
                                        <button type="submit" class="button button-small"><?php esc_html_e( 'Backfill', 'brainwerk-seo-suite' ); ?></button>
                                    </form>
                                <?php else : ?>
                                    &mdash;
                                <?php endif; ?>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            <?php endif; ?>
        </div>
    <?php endif; ?>
</div>

<!-- ====================================================== Determinism ==== -->
<div class="seoforge-card">
    <h2><?php esc_html_e( 'Determinism self-check', 'brainwerk-seo-suite' ); ?></h2>
    <p class="description"><?php esc_html_e( 'Recomputes one day’s history row twice from the same raw data, without writing anything, and checks the result is byte-identical both times. A pass proves the capture pipeline has no hidden nondeterminism; it does not re-verify the integrity chain — use "Run chain verify" above for that.', 'brainwerk-seo-suite' ); ?></p>

    <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:flex;align-items:flex-end;gap:14px;flex-wrap:wrap;">
        <?php wp_nonce_field( Seoforge_Verify::NONCE ); ?>
        <input type="hidden" name="action" value="seoforge_verify_run_determinism">
        <input type="hidden" name="target_blog_id" value="<?php echo esc_attr( $target_blog_id ); ?>">
        <div>
            <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;"><?php esc_html_e( 'Date', 'brainwerk-seo-suite' ); ?></label>
            <input type="date" name="date" value="<?php echo esc_attr( gmdate( 'Y-m-d', time() - DAY_IN_SECONDS ) ); ?>" max="<?php echo esc_attr( gmdate( 'Y-m-d', time() - DAY_IN_SECONDS ) ); ?>">
        </div>
        <button type="submit" class="button button-primary"><?php esc_html_e( 'Run determinism check', 'brainwerk-seo-suite' ); ?></button>
    </form>

    <?php if ( ! empty( $state['determinism'] ) ) : ?>
        <p style="margin-top:14px;">
            <?php if ( $state['determinism']['ok'] ) : ?>
                <span style="color:#16a34a;">✓ <?php
                    printf(
                        /* translators: %s: the date that was recomputed */
                        esc_html__( 'Deterministic — %s recomputed identically twice.', 'brainwerk-seo-suite' ),
                        esc_html( $state['determinism']['date'] )
                    );
                ?></span>
            <?php else : ?>
                <span style="color:#dc2626;">✗ <?php
                    printf(
                        /* translators: %s: the date that failed to recompute identically */
                        esc_html__( 'Nondeterministic — %s produced different content on two consecutive computations.', 'brainwerk-seo-suite' ),
                        esc_html( $state['determinism']['date'] )
                    );
                ?></span>
            <?php endif; ?>
        </p>
    <?php endif; ?>
</div>

<!-- ==================================================== Reconciliation ==== -->
<div class="seoforge-card">
    <h2><?php esc_html_e( 'Nginx access-log reconciliation', 'brainwerk-seo-suite' ); ?></h2>
    <p class="description">
        <?php esc_html_e( 'Compares your raw nginx access log against what Brainwerk SEO Suite actually counted, layer by layer: total log lines, then after removing static-asset requests, then after removing admin/AJAX/REST-shaped requests, then after removing known bots — what remains is compared like-for-like against the plugin\'s own visit count for the same period.', 'brainwerk-seo-suite' ); ?>
    </p>
    <p class="description">
        <?php esc_html_e( 'Two known limits, inherent to log-based reconciliation and not fixable by better parsing: a raw access log carries no signal for a logged-in-admin exclusion or a Do-Not-Track header, so those two live-tracker exclusions cannot be reproduced here. The admin/AJAX/REST layer is also only a path-shape approximation (wp-admin, wp-login.php, wp-cron.php, xmlrpc.php, the REST prefix, plus your configured excluded paths) — a raw log has no is_admin()/wp_doing_ajax()/REST-context signal to check directly.', 'brainwerk-seo-suite' ); ?>
    </p>

    <h3><?php esc_html_e( 'Log source', 'brainwerk-seo-suite' ); ?></h3>
    <form method="post" action="<?php echo esc_url( admin_url( 'admin.php?page=' . Seoforge_Admin::MENU_SLUG . '&tab=verify' ) ); ?>">
        <?php wp_nonce_field( Seoforge_Admin::NONCE ); ?>
        <input type="hidden" name="seoforge_action" value="save_settings">
        <?php if ( $can_network ) : ?>
            <input type="hidden" name="seoforge_tab_keys[]" value="verify_nginx_log_path">
        <?php endif; ?>
        <input type="hidden" name="seoforge_tab_keys[]" value="verify_nginx_log_format">
        <table class="form-table">
            <?php if ( $can_network ) : ?>
            <tr>
                <th><?php esc_html_e( 'Server log path', 'brainwerk-seo-suite' ); ?></th>
                <td>
                    <input type="text" class="regular-text" name="seoforge[verify_nginx_log_path]" value="<?php echo esc_attr( (string) Seoforge_Settings::get( 'verify_nginx_log_path', '' ) ); ?>" placeholder="/var/log/nginx/access.log">
                    <p class="description"><?php esc_html_e( 'An absolute path readable by the PHP-FPM/web-server user. On a stock nginx box that user typically cannot read /var/log/nginx by default — if reconciliation reports "not readable", fix that file\'s permissions, or use the upload option below instead. Plain or .gz.', 'brainwerk-seo-suite' ); ?></p>
                </td>
            </tr>
            <?php else : ?>
                <input type="hidden" name="seoforge[verify_nginx_log_path]" value="<?php echo esc_attr( (string) Seoforge_Settings::get( 'verify_nginx_log_path', '' ) ); ?>">
                <tr><th><?php esc_html_e( 'Server log path', 'brainwerk-seo-suite' ); ?></th><td><em><?php esc_html_e( 'Only a network administrator can set an arbitrary server file path here — use the upload option below instead.', 'brainwerk-seo-suite' ); ?></em></td></tr>
            <?php endif; ?>
            <tr>
                <th><?php esc_html_e( 'Log format', 'brainwerk-seo-suite' ); ?></th>
                <td>
                    <select name="seoforge[verify_nginx_log_format]" id="seoforge-verify-format">
                        <option value="combined" <?php selected( Seoforge_Settings::get( 'verify_nginx_log_format', 'combined' ), 'combined' ); ?>><?php esc_html_e( 'combined', 'brainwerk-seo-suite' ); ?></option>
                        <option value="combined_xff" <?php selected( Seoforge_Settings::get( 'verify_nginx_log_format', 'combined' ), 'combined_xff' ); ?>><?php esc_html_e( 'combined + X-Forwarded-For', 'brainwerk-seo-suite' ); ?></option>
                    </select>
                </td>
            </tr>
        </table>
        <?php submit_button( __( 'Save log settings', 'brainwerk-seo-suite' ) ); ?>
    </form>

    <p>
        <?php esc_html_e( 'Or upload a log file (e.g. if the server path above isn\'t readable):', 'brainwerk-seo-suite' ); ?>
        <?php if ( ! empty( $state['uploaded_log_filename'] ) ) : ?>
            <br><span class="description"><?php
                printf(
                    /* translators: %s: uploaded log file name currently on record */
                    esc_html__( 'Currently on file: %s', 'brainwerk-seo-suite' ),
                    '<code>' . esc_html( $state['uploaded_log_filename'] ) . '</code>'
                );
            ?></span>
        <?php endif; ?>
    </p>
    <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data" style="display:flex;align-items:center;gap:10px;">
        <?php wp_nonce_field( Seoforge_Verify::NONCE ); ?>
        <input type="hidden" name="action" value="seoforge_verify_upload_log">
        <input type="file" name="verify_log_file" accept=".log,.gz" required>
        <button type="submit" class="button"><?php esc_html_e( 'Upload', 'brainwerk-seo-suite' ); ?></button>
    </form>
    <p class="description"><?php esc_html_e( 'Nginx-site owners: this upload directory is blocked from PHP execution via a bundled index.php/.htaccess, but for defence in depth you should also block wp-content/uploads/seoforge-verify/ directly at your webserver config.', 'brainwerk-seo-suite' ); ?></p>

    <h3><?php esc_html_e( 'Run reconciliation', 'brainwerk-seo-suite' ); ?></h3>
    <p class="description"><?php esc_html_e( 'Runs against this site only, in time-bounded chunks so a wide range never risks a PHP timeout. Enter the start and end of the window — both date and time, in this site\'s configured timezone.', 'brainwerk-seo-suite' ); ?></p>

    <div style="display:flex;align-items:flex-end;gap:14px;flex-wrap:wrap;">
        <div>
            <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;"><?php esc_html_e( 'From', 'brainwerk-seo-suite' ); ?></label>
            <input type="datetime-local" id="sf-verify-start" value="<?php echo esc_attr( wp_date( 'Y-m-d\T00:00' ) ); ?>">
        </div>
        <div>
            <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;"><?php esc_html_e( 'To', 'brainwerk-seo-suite' ); ?></label>
            <input type="datetime-local" id="sf-verify-end" value="<?php echo esc_attr( wp_date( 'Y-m-d\TH:i' ) ); ?>">
        </div>
        <div>
            <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;"><?php esc_html_e( 'Format', 'brainwerk-seo-suite' ); ?></label>
            <select id="sf-verify-run-format">
                <option value="combined" <?php selected( Seoforge_Settings::get( 'verify_nginx_log_format', 'combined' ), 'combined' ); ?>><?php esc_html_e( 'combined', 'brainwerk-seo-suite' ); ?></option>
                <option value="combined_xff" <?php selected( Seoforge_Settings::get( 'verify_nginx_log_format', 'combined' ), 'combined_xff' ); ?>><?php esc_html_e( 'combined + X-Forwarded-For', 'brainwerk-seo-suite' ); ?></option>
            </select>
        </div>
        <div>
            <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;"><?php esc_html_e( 'Source', 'brainwerk-seo-suite' ); ?></label>
            <select id="sf-verify-source">
                <?php if ( ! is_multisite() || $can_network ) : ?>
                    <option value="configured"><?php esc_html_e( 'Configured server path', 'brainwerk-seo-suite' ); ?></option>
                <?php endif; ?>
                <option value="uploaded"><?php esc_html_e( 'Uploaded file', 'brainwerk-seo-suite' ); ?></option>
            </select>
        </div>
        <button type="button" id="sf-verify-run" class="button button-primary"><?php esc_html_e( 'Run reconciliation', 'brainwerk-seo-suite' ); ?></button>
        <button type="button" id="sf-verify-cancel" class="button" style="display:none;"><?php esc_html_e( 'Cancel', 'brainwerk-seo-suite' ); ?></button>
    </div>

    <p id="sf-verify-progress" style="margin-top:10px;font-size:13px;"></p>

    <div id="sf-verify-results" style="display:none;margin-top:14px;">
        <p style="font-size:20px;font-weight:700;" id="sf-verify-deviation"></p>
        <table class="widefat striped" style="max-width:640px;">
            <tbody>
                <tr><th style="width:320px;"><?php esc_html_e( 'Total log lines in range', 'brainwerk-seo-suite' ); ?></th><td id="sf-verify-total"></td></tr>
                <tr><th><?php esc_html_e( 'After removing static assets', 'brainwerk-seo-suite' ); ?></th><td id="sf-verify-after-static"></td></tr>
                <tr><th><?php esc_html_e( 'After removing admin/AJAX/REST-shaped requests', 'brainwerk-seo-suite' ); ?></th><td id="sf-verify-after-admin"></td></tr>
                <tr><th><?php esc_html_e( 'After removing known bots (log-side count)', 'brainwerk-seo-suite' ); ?></th><td id="sf-verify-counted"></td></tr>
                <tr><th><?php esc_html_e( 'Plugin\'s own visit count, same window', 'brainwerk-seo-suite' ); ?></th><td id="sf-verify-plugin-count"></td></tr>
                <tr><th><?php esc_html_e( 'Malformed log lines (skipped)', 'brainwerk-seo-suite' ); ?></th><td id="sf-verify-malformed"></td></tr>
            </tbody>
        </table>
        <div style="display:flex;gap:24px;flex-wrap:wrap;margin-top:14px;">
            <div style="min-width:260px;">
                <h4><?php esc_html_e( 'Sample: seen in log, not by the plugin', 'brainwerk-seo-suite' ); ?></h4>
                <ul id="sf-verify-log-only" style="font-family:monospace;font-size:12px;"></ul>
            </div>
            <div style="min-width:260px;">
                <h4><?php esc_html_e( 'Sample: seen by the plugin, not in log', 'brainwerk-seo-suite' ); ?></h4>
                <ul id="sf-verify-plugin-only" style="font-family:monospace;font-size:12px;"></ul>
            </div>
        </div>
    </div>
</div>
