<?php

namespace CleantalkSP\SpbctWP\VulnerabilityAlarm;

use CleantalkSP\SpbctWP\VulnerabilityAlarm\Dto\PluginReport;

class SpbcPluginInstallListTable extends \WP_Plugin_Install_List_Table
{
    const SESSION_TRANSIENT_PREFIX = 'spbc_psc_tab_session_';

    const FILTER_ALL = 'all';

    const FILTER_INSTALLED = 'installed';

    /**
     * @var array<string, array{slug: string, id: string, psc: string, report_object: PluginReport}>
     */
    private $psc_badges_by_slug = [];

    /**
     * @var int
     */
    private $catalog_total = 0;

    /**
     * @var int
     */
    private $next_api_page = 1;

    /**
     * @var int
     */
    private $next_page = 2;

    /**
     * @var bool
     */
    private $has_more = false;

    /**
     * @var string
     */
    private $filter = self::FILTER_ALL;

    /**
     * @var int
     */
    private $requested_api_page = 0;

    /**
     * @var string
     */
    private $search_term = '';

    public function __construct()
    {
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;

        parent::__construct([
            'plural' => 'plugins',
            'screen' => $screen ?: 'plugin-install',
        ]);
    }

    /**
     * @param string $filter
     * @return void
     */
    public function setFilter($filter)
    {
        $this->filter = $filter === self::FILTER_INSTALLED ? self::FILTER_INSTALLED : self::FILTER_ALL;
    }

    /**
     * Research API page to continue from, provided by the previous response.
     *
     * @param int $api_page
     * @return void
     */
    public function setRequestedApiPage($api_page)
    {
        $this->requested_api_page = max(0, (int) $api_page);
    }

    /**
     * @param string $term
     * @return void
     */
    public function setSearch($term)
    {
        $this->search_term = sanitize_text_field(trim($term));
    }

    /**
     * @return void
     */
    public function prepare_items() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
    {
        if ($this->search_term !== '') {
            $this->prepareSearchResults();
            return;
        }

        if ($this->filter === self::FILTER_INSTALLED) {
            $this->prepareInstalledItems();
            return;
        }

        $per_page = PscCertifiedPluginsApi::PER_PAGE;

        // api_page carries the real DB offset for cursor-based pagination.
        $db_offset = $this->requested_api_page > 0 ? $this->requested_api_page : 0;

        $this->preparePageFromCache($db_offset, $per_page);
    }

    /**
     * @return void
     */
    private function prepareSearchResults()
    {
        $result = PscCertifiedPluginsCache::searchByName($this->search_term);

        $this->psc_badges_by_slug = $result['badges'];
        $this->items = $result['plugins'];
        $this->catalog_total = count($result['plugins']);
        $this->next_page = 1;
        $this->next_api_page = 0;
        $this->has_more = false;

        $this->set_pagination_args([
            'total_items' => $this->catalog_total,
            'per_page'    => max(1, $this->catalog_total),
            'total_pages' => 1,
        ]);
    }

    /**
     * @return void
     */
    private function prepareInstalledItems()
    {
        $cached = PscCertifiedPluginsCache::getInstalledPlugins();

        $this->psc_badges_by_slug = $cached['badges'];
        $this->items = $cached['plugins'];
        $this->catalog_total = count($cached['plugins']);
        $this->next_page = 1;
        $this->next_api_page = 0;
        $this->has_more = false;

        $this->set_pagination_args([
            'total_items' => $this->catalog_total,
            'per_page'    => max(1, $this->catalog_total),
            'total_pages' => 1,
        ]);
    }

    /**
     * @param int $db_offset
     * @param int $per_page
     * @return void
     */
    private function preparePageFromCache($db_offset, $per_page)
    {
        $result = PscCertifiedPluginsCache::getPageWithEnrichment($db_offset, $per_page);
        $total_rows = PscCertifiedPluginsCache::countAll();

        $this->psc_badges_by_slug = $result['badges'];
        $this->items = $result['plugins'];
        $this->catalog_total = $total_rows;
        $this->next_page = 0;
        $this->next_api_page = $result['db_offset'];
        $this->has_more = $result['db_offset'] < $total_rows;

        $this->set_pagination_args([
            'total_items' => $total_rows,
            'per_page'    => $per_page,
            'total_pages' => (int) ceil(max(1, $total_rows) / $per_page),
        ]);
    }

    /**
     * @return int
     */
    public function getNextPage()
    {
        return $this->next_page;
    }

    /**
     * @return int
     */
    public function getNextApiPage()
    {
        return $this->next_api_page;
    }

    /**
     * @return bool
     */
    public function has_more() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
    {
        return $this->has_more;
    }

    /**
     * @return string
     */
    public function get_rows_html() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
    {
        return $this->withPscBadgeFilters(function () {
            ob_start();
            $this->display_rows_or_placeholder();

            return (string) ob_get_clean();
        });
    }

    /**
     * @return void
     */
    public function display()
    {
        $this->withPscBadgeFilters(function () {
            parent::display();
        });
    }

    /**
     * @param callable(): mixed $callback
     * @return mixed
     */
    private function withPscBadgeFilters($callback)
    {
        add_filter('plugin_install_action_links', [$this, 'filterPluginInstallActionLinks'], 10, 2);

        try {
            return $callback();
        } finally {
            remove_filter('plugin_install_action_links', [$this, 'filterPluginInstallActionLinks'], 10);
        }
    }

    /**
     * @param string[] $action_links
     * @param array|object $plugin
     * @return string[]
     * @psalm-suppress PossiblyUnusedReturnValue
     */
    public function filterPluginInstallActionLinks($action_links, $plugin)
    {
        $slug = VulnerabilityAlarm::getPluginSlug($plugin);

        if (empty($slug) || empty($this->psc_badges_by_slug[$slug])) {
            return $action_links;
        }

        $safe_plugin_data = $this->psc_badges_by_slug[$slug];
        $report = $safe_plugin_data['report_object'];
        $research_url = $report instanceof PluginReport
            ? (string) $report->research_url
            : '';

        $badge = VulnerabilityAlarm::showSafeBadge(
            '',
            $safe_plugin_data['slug'],
            $safe_plugin_data['id'],
            $safe_plugin_data['psc'],
            $research_url
        );

        if (! empty($badge)) {
            $action_links[] = $badge;
        }

        return $action_links;
    }

    /**
     * @return void
     */
    public static function clearSession()
    {
        delete_transient(self::getSessionTransientKey());
    }

    /**
     * @return string
     */
    private static function getSessionTransientKey()
    {
        return self::SESSION_TRANSIENT_PREFIX . get_current_user_id();
    }

    /**
     * @param string $which
     * @return void
     */
    protected function display_tablenav($which) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
    {
        if ('top' !== $which) {
            return;
        }

        wp_referer_field();
    }
}
