<?php
declare(strict_types=1);

namespace SwapAds\Client\Admin;

use SwapAds\Client\Admin\OperatorDashboardHubPage;
use SwapAds\Client\License\LicenseManager;
use SwapAds\Client\Http\Redirect;
use SwapAds\Client\Http\Request;

/**
 * BacklinksHubPage — 2-tab UI for early-adopters launch (TODO-CLIENT-007, 2026-08-02).
 *
 * Tab structure (replaces the previous 3-tab "Your + Place + Approvals" hub):
 *   - **My Backlinks** (TAB_MY, default): the operator's own backlink offers.
 *     Source URLs clickable + open in new tab. Per-row actions: Retract (pause),
 *     Reactivate (resume), Delete (soft-delete), Check still alive (recheck source).
 *     Top: "Create new backlink" button + summary metrics + create form.
 *   - **Partner Links** (TAB_PARTNER): incoming partner backlinks.
 *     - *Suggestions* (top): partners who want to place a backlink on your site.
 *       Approve / Reject / Refresh list. Shows audience + definition.
 *     - *Approved backlinks* (grid): backlinks you've selected to show on your
 *       site. Each row: source URL (clickable, opens new tab), anchor, clicks, views.
 *       Unapprove action removes from the displayed list.
 *
 * Replaces:
 *   - SourceCheckPage (now a section inside My Backlinks)
 *   - BacklinkApprovalPage (now a section inside Partner Links)
 *   - ApprovalsHubPage (now a section inside Partner Links)
 *
 * The source pages still exist (for back-compat) but their `addMenu()` calls
 * are no-ops (handled via CSS hide in admin_head).
 *
 * @since 1.5.10
 */
final class BacklinksHubPage
{
    public const MENU_SLUG = 'swapads-client-backlinks-hub';

    public const TAB_MY      = 'my';
    public const TAB_PARTNER = 'partner';

    /** Legacy tab constants (kept for back-compat with bookmarks). */
    public const TAB_YOUR      = 'my';    // alias
    public const TAB_PLACE     = 'partner'; // alias
    public const TAB_APPROVALS = 'partner'; // alias

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

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

    /**
     * Hide the legacy Source Check / Approvals submenu links from the sidebar.
     *
     * TODO-CLIENT-007 (2026-08-02): those submenus are now embedded as
     * sections inside the Backlinks Hub. The legacy pages still exist
     * (for bookmark-safe URLs) but the sidebar links are misleading.
     *
     * F2XX-fix 2026-08-01: use CSS hide instead of remove_submenu_page()
     * (which broke the cap check in WP 7.0).
     *
     * @since 1.5.10
     */
    public static function hideLegacySubmenus(): void
    {
        // F2XX-fix 2026-08-02: use anchored selectors (`$=`) instead of `*=`
        // substring matches. The old `href*="page=swapads-client-approvals"` would
        // also match `swapads-client-approvals-hub` (a DIFFERENT slug) and hide it.
        echo '<style>'
            . '#adminmenu a[href$="page=swapads-client-source-check"],'
            . '#adminmenu a[href$="page=swapads-client-approvals\""] {'
            . 'display:none !important;'
            . '}'
            . '</style>';
    }

    /**
     * Render the tab nav HTML (2 tabs for the early-adopters launch).
     */
    public static function renderTabs(string $active): string
    {
        $my      = esc_url(add_query_arg('tab', self::TAB_MY));
        $partner = esc_url(add_query_arg('tab', self::TAB_PARTNER));
        $h  = '<nav class="swapads-tab-nav" role="tablist" aria-label="Backlinks tabs">';
        $h .= '<a href="' . $my . '" role="tab" aria-selected="' . ($active === self::TAB_MY ? 'true' : 'false') . '"'
            . ' class="swapads-tab' . ($active === self::TAB_MY ? ' swapads-tab--active' : '') . '">'
            . esc_html__('My Backlinks', 'swapads-client') . '</a>';
        $h .= '<a href="' . $partner . '" role="tab" aria-selected="' . ($active === self::TAB_PARTNER ? 'true' : 'false') . '"'
            . ' class="swapads-tab' . ($active === self::TAB_PARTNER ? ' swapads-tab--active' : '') . '">'
            . esc_html__('Partner Links', 'swapads-client') . '</a>';
        $h .= '</nav>';
        return $h;
    }

    /**
     * Register the single "Backlinks" submenu.
     */
    public static function addMenu(): void
    {
        add_submenu_page(
            OperatorDashboardHubPage::MENU_SLUG,
            'Backlinks',
            'Backlinks',
            'manage_options',
            self::MENU_SLUG,
            [self::class, 'render']
        );
    }

    /**
     * Render the full hub page.
     */
    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__('Backlinks', 'swapads-client') . '</h1>';

        if ($tab === self::TAB_MY) {
            echo '<p class="description">'
                . esc_html__('Your backlink offers. Each offer is checked daily; auto-paused on 404 or no content.', 'swapads-client')
                . '</p>';
        } else {
            echo '<p class="description">'
                . esc_html__('Manage backlinks on your site from partners. Approve incoming suggestions and review approved backlinks.', 'swapads-client')
                . '</p>';
        }

        echo self::renderTabs($tab);

        if (!LicenseManager::isLicensed()) {
            echo Renderer::card(
                'License required',
                '<p>You need an active license before managing backlinks.</p>'
                    . '<p><a href="' . esc_url(admin_url('admin.php?page=' . SettingsPage::MENU_SLUG)) . '" class="button">Activate license</a></p>',
                'swapads-card-warning'
            );
            echo '</div>'; // .wrap
            return;
        }

        if ($tab === self::TAB_PARTNER) {
            self::renderPartnerLinks();
        } else {
            self::renderMyBacklinks();
        }

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

    /**
     * Render the "My Backlinks" tab content.
     *
     * TODO-CLIENT-007 (2026-08-02): the legacy Source Check overview.
     * Source URLs are clickable + open in new tab. Per-row actions:
     * Retract (pause), Reactivate (resume), Delete (soft-delete),
     * Check still alive (recheck source). View statistics (bots + users).
     *
     * Top of section: "Create new backlink" button.
     */
    public static function renderMyBacklinks(): void
    {
        // "Create new backlink" button at top
        $createUrl = esc_url(admin_url('admin.php?page=' . BacklinkCreatePage::MENU_SLUG . '&created=1'));
        echo '<p style="margin: 12px 0;">'
            . '<a href="' . $createUrl . '" class="button button-primary">' . esc_html__('Create new backlink', 'swapads-client') . ' +</a>'
            . ' <a href="' . esc_url(admin_url('admin.php?page=' . BacklinkCreatePage::MENU_SLUG)) . '" class="button">' . esc_html__('View create form', 'swapads-client') . '</a>'
            . '</p>';

        // Delegate to the existing SourceCheckPage render
        SourceCheckPage::renderSection();
    }

    /**
     * Render the "Partner Links" tab content.
     *
     * TODO-CLIENT-007 (2026-08-02): the legacy Review Suggestions section
     * (now renamed to "Partner Links") + a new grid of approved backlinks.
     */
    public static function renderPartnerLinks(): void
    {
        // Top: suggestions (incoming partner backlink requests)
        BacklinkApprovalPage::renderSuggestionsCard();

        // Below: approved backlinks grid (the ones selected by the operator
        // to show on their site)
        echo '<h2 style="margin-top: 24px;">' . esc_html__('Approved Backlinks (showing on your site)', 'swapads-client') . '</h2>';
        self::renderApprovedBacklinksGrid();
    }

    /**
     * Render the grid of approved backlinks (showing on operator's site).
     *
     * Each row shows:
     *   - Source URL (clickable, opens in new tab)
     *   - Anchor text
     *   - Status badge (active / paused / removed)
     *   - Last viewed at (when a real visitor last landed on the page)
     *   - Placement count (how many partner sites have approved this backlink)
     *   - Unapprove action (removes from this display)
     *
     * Note: views/clicks bot-vs-user split is not yet fetched per-backlink
     * (the /v1/backlinks/statistics/summary endpoint returns site-wide
     * aggregates). For the early-adopters launch, the actionable info is
     * "when was this last viewed?" + "how many partners placed it?". The
     * full stats split will arrive in TODO-MVP-018 in the post-MLP sprint.
     *
     * @since 1.5.10
     */
    private static function renderApprovedBacklinksGrid(): void
    {
        $client = \SwapAds\Client\Api\RestClient::fromOption();
        try {
            $resp = $client->listBacklinks(true, 100);
        } catch (\Throwable $e) {
            echo Renderer::card(
                'Could not load approved backlinks',
                '<p>' . esc_html($e->getMessage()) . '</p>',
                'swapads-card-error'
            );
            return;
        }

        if (!is_array($resp) || empty($resp['success'])) {
            echo Renderer::card(
                'No backlinks yet',
                '<p>When you create your first backlink offer, it will appear here. Approved backlinks from partners are shown on your site via the SwapAds block or shortcode.</p>',
                'swapads-card-info'
            );
            return;
        }

        // Server returns 'backlinks' (not 'items'). Accept both for backwards-compat.
        $items = $resp['backlinks'] ?? $resp['items'] ?? [];
        if (!is_array($items) || count($items) === 0) {
            echo Renderer::card(
                'No backlinks yet',
                '<p>When you create your first backlink offer, it will appear here.</p>',
                'swapads-card-info'
            );
            return;
        }

        $html  = '<table class="widefat striped swapads-table">';
        $html .= '<thead><tr>';
        $html .= '<th>Source URL</th>';
        $html .= '<th>Anchor</th>';
        $html .= '<th>Status</th>';
        $html .= '<th style="width:140px;">Last viewed</th>';
        $html .= '<th style="width:100px;">Placements</th>';
        $html .= '<th style="width:140px;">Action</th>';
        $html .= '</tr></thead><tbody>';

        foreach ($items as $it) {
            $id = (int) ($it['id'] ?? 0);
            $sourceUrl = (string) ($it['source_url'] ?? '');
            $anchor = (string) ($it['anchor_text'] ?? '');
            $status = (string) ($it['status'] ?? '');
            $lastViewedAt = (string) ($it['last_viewed_at'] ?? '');
            $placementCount = (int) ($it['placement_count'] ?? 0);

            $html .= '<tr>';
            $html .= '<td>';
            if ($sourceUrl !== '') {
                $html .= '<a href="' . esc_url($sourceUrl) . '" target="_blank" rel="noopener noreferrer">'
                    . esc_html(self::truncateUrl($sourceUrl, 60))
                    . '<span class="screen-reader-text"> (opens in new tab)</span></a>';
            }
            $html .= '</td>';
            $html .= '<td>' . esc_html($anchor) . '</td>';
            $html .= '<td>' . self::renderStatusBadge($status) . '</td>';
            $html .= '<td><small>' . esc_html($lastViewedAt !== '' ? $lastViewedAt : '—') . '</small></td>';
            $html .= '<td>' . esc_html((string) $placementCount) . '</td>';
            $html .= '<td>';
            if ($id > 0 && $status !== 'removed') {
                $html .= self::renderUnapproveForm($id);
            }
            $html .= '</td>';
            $html .= '</tr>';
        }

        $html .= '</tbody></table>';
        echo $html;
    }

    /**
     * Render a status badge for the approved backlinks grid.
     *
     * @since 1.5.10
     */
    private static function renderStatusBadge(string $status): string
    {
        $colors = [
            'active'   => '#46b450',
            'paused'   => '#dba617',
            'removed'  => '#666',
            'depleted' => '#996633',
        ];
        $color = $colors[$status] ?? '#666';
        return sprintf(
            '<span style="display:inline-block;padding:2px 8px;border-radius:3px;background:%s;color:#fff;font-size:11px;font-weight:600;">%s</span>',
            esc_attr($color),
            esc_html($status !== '' ? $status : 'unknown')
        );
    }

    /**
     * Render the "Unapprove" form for a single approved backlink.
     *
     * @since 1.5.10
     */
    private static function renderUnapproveForm(int $backlinkId): string
    {
        $action = esc_url(admin_url('admin-post.php'));
        $nonce  = wp_create_nonce('swapads_client_unapprove_approved');
        $back = esc_url(add_query_arg('tab', self::TAB_PARTNER, admin_url('admin.php?page=' . self::MENU_SLUG)));
        return sprintf(
            '<form method="post" action="%s" style="display:inline-block;">
                <input type="hidden" name="action" value="swapads_client_unapprove_approved">
                <input type="hidden" name="_wpnonce" value="%s">
                <input type="hidden" name="backlink_id" value="%d">
                <input type="hidden" name="_redirect_back" value="%s">
                <button type="submit" class="button button-small">Unapprove</button>
            </form>',
            $action,
            esc_attr($nonce),
            $backlinkId,
            esc_attr($back)
        );
    }

    /**
     * Handle the "Unapprove" form POST.
     *
     * Calls the server endpoint to update the backlink status to "rejected" (or
     * "unapproved"). Idempotent. On success, redirects back to the
     * Partner Links tab.
     *
     * @since 1.5.10
     */
    public static function handleUnapproveApproved(): void
    {
        if (!current_user_can('manage_options')) {
            wp_die('Insufficient permissions', 'Forbidden', ['response' => 403]);
        }
        check_admin_referer('swapads_client_unapprove_approved');

        $backlinkId = Request::inputInt('backlink_id', 0, 'post');
        $back = Request::input('_redirect_back', '', 'post');

        if ($backlinkId <= 0) {
            Redirect::to(add_query_arg('tab', self::TAB_PARTNER, admin_url('admin.php?page=' . self::MENU_SLUG)));
            return;
        }

        $client = \SwapAds\Client\Api\RestClient::fromOption();
        try {
            $resp = $client->rejectBacklink($backlinkId, 'unapproved by operator');
        } catch (\Throwable $e) {
            set_transient(self::MENU_SLUG . '_flash', [
                'error_code' => 'UNAPPROVE_FAILED',
                'message'    => 'Could not unapprove: ' . $e->getMessage(),
                'http_status' => 0,
                'when'       => time(),
            ], MINUTE_IN_SECONDS);
            Redirect::to($back !== '' ? $back : add_query_arg('tab', self::TAB_PARTNER, admin_url('admin.php?page=' . self::MENU_SLUG)));
            return;
        }

        if (is_array($resp) && !empty($resp['success'])) {
            set_transient(self::MENU_SLUG . '_flash', [
                'error_code' => 'UNAPPROVED',
                'message'    => 'Backlink unapproved.',
                'http_status' => 200,
                'when'       => time(),
            ], MINUTE_IN_SECONDS);
        } else {
            set_transient(self::MENU_SLUG . '_flash', [
                'error_code' => 'UNAPPROVE_FAILED',
                'message'    => 'Server returned an error.',
                'http_status' => 500,
                'when'       => time(),
            ], MINUTE_IN_SECONDS);
        }

        Redirect::to($back !== '' ? $back : add_query_arg('tab', self::TAB_PARTNER, admin_url('admin.php?page=' . self::MENU_SLUG)));
    }

    /**
     * Truncate a URL for display.
     *
     * @since 1.5.10
     */
    private static function truncateUrl(string $url, int $max = 60): string
    {
        if (strlen($url) <= $max) {
            return $url;
        }
        return substr($url, 0, $max - 3) . '...';
    }
}
