<?php
/**
 * Activity Log Admin Page (F230).
 *
 * Operator-facing view of their server-side audit log.
 *
 * Surfaces the wp_swapads_audit_log entries (which have been written
 * since F230) so the operator can see exactly what happened on their
 * account: when a credit was spent, when a backlink was approved /
 * rejected, when source-check ran and what it found.
 *
 * Audit log is critical for trust in a 1:1 barter exchange — operators
 * must be able to see "who did what" so a compromised admin cookie or
 * buggy automation can't drain credits without notice.
 *
 * @package SwapAds\Client\Admin
 * @since   1.5.2
 */

declare(strict_types=1);

namespace SwapAds\Client\Admin;

use SwapAds\Client\Admin\OperatorDashboardHubPage;
use SwapAds\Client\Api\RestClient;
use SwapAds\Client\License\LicenseManager;
use SwapAds\Client\Http\Request;

/**
 * Class ActivityLogPage.
 *
 * @since 1.5.2
 */
final class ActivityLogPage
{
    public const MENU_SLUG     = 'swapads-client-activity-log';
    public const ERROR_CONTEXT = 'swapads_client_activity_log';
    public const PER_PAGE      = 50;

    /**
     * Register WP hooks.
     */
    public static function register(): void
    {
        add_action('admin_menu', [self::class, 'addMenu']);
    }

    /**
     * Register the admin page (sub-menu of SwapAds).
     */
    public static function addMenu(): void
    {
        add_submenu_page(
            OperatorDashboardHubPage::MENU_SLUG,
            'Activity Log',
            'Activity Log',
            'manage_options',
            self::MENU_SLUG,
            [self::class, 'render']
        );
    }

    /**
     * Render the activity log page.
     */
    public static function render(): void
    {
        if (!current_user_can('manage_options')) {
            wp_die('Insufficient permissions', 'Forbidden', ['response' => 403]);
        }
        $licensed = LicenseManager::isLicensed();
        if (!$licensed) {
            echo Renderer::pageHeader(
                'Activity Log',
                'Every state-changing action on your account is recorded here.'
            );
            echo Renderer::card(
                'License required',
                '<p>You need an active license before activity events are recorded.</p>'
                    . '<p><a href="' . esc_url(admin_url('admin.php?page=' . OperatorDashboardHubPage::MENU_SLUG))
                    . '" class="button">Activate license</a></p>',
                'swapads-card-warning'
            );
            echo Renderer::pageFooter();
            return;
        }

        $rest   = RestClient::fromOption();
        $action = Request::input('action_filter', '', 'get');
        $type   = Request::input('entity_filter', '', 'get');
        $offset = max(0, Request::inputInt('offset', 0, 'get'));
        $resp   = $rest->listAuditLog($action, $type, self::PER_PAGE, $offset);
        $items  = isset($resp['items']) && is_array($resp['items']) ? $resp['items'] : [];
        $total  = isset($resp['total']) ? (int) $resp['total'] : count($items);
        $error  = $rest->lastError();

        echo Renderer::pageHeader(
            'Activity Log',
            sprintf(
                '%s events recorded. Newest first.',
                number_format_i18n($total)
            )
        );

        // Surface any inline error.
        if ($error !== null) {
            echo ClientErrorRenderer::render(self::ERROR_CONTEXT, [
                'code'    => 'BACKEND_UNREACHABLE',
                'message' => (string) wp_json_encode($error),
            ], 'load activity log');
        }

        // Filters row
        echo '<form method="get" class="swapads-filter-bar" style="margin-bottom:16px;">';
        echo '<input type="hidden" name="page" value="' . esc_attr(self::MENU_SLUG) . '">';
        echo '<label style="margin-right:12px;">Action: ';
        echo '<select name="action_filter">';
        $opts = [
            '' => 'All actions',
            'backlink.offer'        => 'backlink.offer',
            'backlink.remove'       => 'backlink.remove',
            'backlink.update'       => 'backlink.update',
            'approval.pending'      => 'approval.pending',
            'approval.reject'       => 'approval.reject',
            'source_check.ok'       => 'source_check.ok',
            'source_check.404'      => 'source_check.404',
            'source_check.error'    => 'source_check.error',
            'source_check.refund'   => 'source_check.refund',
        ];
        foreach ($opts as $val => $label) {
            printf(
                '<option value="%s"%s>%s</option>',
                esc_attr($val),
                selected($action, $val, false),
                esc_html($label)
            );
        }
        echo '</select></label>';
        echo '<label style="margin-right:12px;">Entity: ';
        echo '<select name="entity_filter">';
        $eopts = [
            ''              => 'All entities',
            'backlink'      => 'backlink',
            'approval'      => 'approval',
            'source_check'  => 'source_check',
            'license'       => 'license',
        ];
        foreach ($eopts as $val => $label) {
            printf(
                '<option value="%s"%s>%s</option>',
                esc_attr($val),
                selected($type, $val, false),
                esc_html($label)
            );
        }
        echo '</select></label>';
        echo '<button type="submit" class="button">Filter</button>';
        echo '</form>';

        if (empty($items)) {
            echo Renderer::card(
                'No activity yet',
                '<p>No matching events. Once you approve, reject, or run source-checks, they will appear here.</p>',
                'swapads-card-info'
            );
        } else {
            // TODO-CLIENT-009 (2026-08-02): build a backlink-id -> source_url
            // lookup map so each row can show the clickable URL.
            $urlMap = self::fetchBacklinkUrlMap($rest);

            echo '<table class="widefat striped" role="table" aria-label="Activity log">';
            echo '<thead><tr>';
            echo '<th scope="col">When</th>';
            echo '<th scope="col">Actor</th>';
            echo '<th scope="col">Action</th>';
            echo '<th scope="col">Entity</th>';
            echo '<th scope="col">Backlink URL</th>';
            echo '<th scope="col">Details</th>';
            echo '</tr></thead><tbody>';
            foreach ($items as $row) {
                $when = (string) ($row['created_at'] ?? '');
                $actor = (string) ($row['actor'] ?? 'system');
                $a = (string) ($row['action'] ?? '');
                $eT = (string) ($row['entity_type'] ?? '');
                $eI = (string) ($row['entity_id'] ?? '');
                $details = $row['details'] ?? null;
                // Resolve backlink URL (if applicable).
                $urlHtml = '<span class="swapads-muted">—</span>';
                if ($eT === 'backlink' && $eI !== '' && isset($urlMap[$eI])) {
                    $url = (string) $urlMap[$eI];
                    $urlHtml = '<a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">'
                        . esc_html(mb_strimwidth($url, 0, 50, '...'))
                        . '<span class="screen-reader-text"> (opens in new tab)</span></a>';
                }
                echo '<tr>';
                echo '<td>' . esc_html(mysql2date('Y-m-d H:i:s', $when)) . '</td>';
                echo '<td><span class="swapads-actor swapads-actor--' . esc_attr($actor) . '">' . esc_html($actor) . '</span></td>';
                echo '<td><code>' . esc_html($a) . '</code></td>';
                echo '<td>' . esc_html($eT) . ($eI !== '' ? ' #' . esc_html($eI) : '') . '</td>';
                echo '<td>' . $urlHtml . '</td>';
                echo '<td>';
                if (is_array($details)) {
                    $json = (string) wp_json_encode($details);
                    echo '<pre style="margin:0; font-size:11px; max-width:480px; overflow-x:auto;">'
                        . esc_html($json)
                        . '</pre>';
                } else {
                    echo '<span class="swapads-muted">—</span>';
                }
                echo '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';

            // Pagination links
            self::renderPagination($offset, count($items), $total, $action, $type);
        }

        echo Renderer::pageFooter();
    }

    /**
     * Render Next / Prev pagination links.
     */

    /**
     * Fetch a backlink-id -> source_url lookup map for the current site.
     *
     * TODO-CLIENT-009 (2026-08-02): used by the activity log to render
     * each row's backlink URL as a clickable link. Returns empty array
     * on server failure (the renderer falls back to "—" for missing rows).
     *
     * @param \SwapAds\Client\Api\RestClient $rest
     * @return array<string, string> Map of backlink_id => source_url.
     */
    private static function fetchBacklinkUrlMap($rest): array
    {
        $map = [];
        try {
            $resp = $rest->listBacklinks(true, 500);
        } catch (\Throwable $e) {
            return $map;
        }
        if (!is_array($resp) || empty($resp['success'])) {
            return $map;
        }
        $items = $resp['backlinks'] ?? $resp['items'] ?? [];
        if (!is_array($items)) {
            return $map;
        }
        foreach ($items as $it) {
            $id = (string) ((int) ($it['id'] ?? 0));
            $url = (string) ($it['source_url'] ?? '');
            if ($id !== '0' && $url !== '') {
                $map[$id] = $url;
            }
        }
        return $map;
    }

    private static function renderPagination(int $offset, int $shown, int $total, string $action, string $type): void
    {
        if ($shown < self::PER_PAGE && $offset === 0) {
            return; // Only one page, no nav needed.
        }
        $baseUrl = admin_url('admin.php?page=' . self::MENU_SLUG);
        $params  = ['action_filter' => $action, 'entity_filter' => $type];

        echo '<div class="tablenav" style="margin-top:12px;"><div class="tablenav-pages">';
        if ($offset > 0) {
            $prev = $offset - self::PER_PAGE;
            $params['offset'] = max(0, $prev);
            echo '<a class="button" href="' . esc_url(add_query_arg($params, $baseUrl)) . '">&laquo; Previous</a> ';
        }
        if ($offset + $shown < $total) {
            $params['offset'] = $offset + self::PER_PAGE;
            echo '<a class="button" href="' . esc_url(add_query_arg($params, $baseUrl)) . '">Next &raquo;</a>';
        }
        echo '</div></div>';
    }
}