<?php
/**
 * 404 log list table: hit counts, pagination, bulk actions — all free
 * (all paywalled by the incumbent this niche is leaving).
 *
 * @package RedirectZen
 */

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

if ( ! class_exists( 'WP_List_Table' ) ) {
	require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}

class RDZ_404_Table extends WP_List_Table {

	/**
	 * Constructor.
	 */
	public function __construct() {
		parent::__construct(
			array(
				'singular' => 'rdz_404',
				'plural'   => 'rdz_404s',
				'ajax'     => false,
			)
		);
	}

	/**
	 * Columns.
	 *
	 * @return array
	 */
	public function get_columns() {
		return array(
			'cb'        => '<input type="checkbox" />',
			'path'      => __( 'URL', 'redirectzen-301-redirect-manager' ),
			'hits'      => __( 'Hits', 'redirectzen-301-redirect-manager' ),
			'last_seen' => __( 'Last seen', 'redirectzen-301-redirect-manager' ),
			'referrer'  => __( 'Referrer', 'redirectzen-301-redirect-manager' ),
		);
	}

	/**
	 * Sortable columns.
	 *
	 * @return array
	 */
	protected function get_sortable_columns() {
		return array(
			'hits'      => array( 'hits', true ),
			'last_seen' => array( 'last_seen', true ),
		);
	}

	/**
	 * Bulk actions.
	 *
	 * @return array
	 */
	protected function get_bulk_actions() {
		return array(
			'ignore' => __( 'Ignore', 'redirectzen-301-redirect-manager' ),
			'delete' => __( 'Delete', 'redirectzen-301-redirect-manager' ),
		);
	}

	/**
	 * Views: Open / Ignored / Resolved.
	 *
	 * @return array
	 */
	protected function get_views() {
		$counts  = RDZ_404_Log::get_counts();
		$base    = admin_url( 'admin.php?page=redirectzen-404s' );
		$current = isset( $_GET['rdz_view'] ) ? sanitize_key( wp_unslash( $_GET['rdz_view'] ) ) : 'open'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		$views = array(
			'open'     => array( __( 'Open', 'redirectzen-301-redirect-manager' ), $counts['open'] ),
			'resolved' => array( __( 'Resolved', 'redirectzen-301-redirect-manager' ), $counts['resolved'] ),
			'ignored'  => array( __( 'Ignored', 'redirectzen-301-redirect-manager' ), $counts['ignored'] ),
		);

		$links = array();
		foreach ( $views as $key => $view ) {
			$class         = ( $current === $key ) ? ' class="current"' : '';
			$links[ $key ] = sprintf(
				'<a href="%s"%s>%s <span class="count">(%s)</span></a>',
				esc_url( add_query_arg( 'rdz_view', $key, $base ) ),
				$class,
				esc_html( $view[0] ),
				number_format_i18n( $view[1] )
			);
		}

		return $links;
	}

	/**
	 * Loads rows.
	 */
	public function prepare_items() {
		global $wpdb;

		$per_page = $this->get_items_per_page( 'rdz_404s_per_page', 25 );
		$paged    = max( 1, $this->get_pagenum() );
		$table    = RDZ_404_Log::table();

		$view   = isset( $_GET['rdz_view'] ) ? sanitize_key( wp_unslash( $_GET['rdz_view'] ) ) : 'open'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$status = 0;
		if ( 'ignored' === $view ) {
			$status = 1;
		} elseif ( 'resolved' === $view ) {
			$status = 2;
		}

		$where  = array( 'status = %d' );
		$params = array( $status );

		$search = isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( '' !== $search ) {
			$where[]  = 'path LIKE %s';
			$params[] = '%' . $wpdb->esc_like( $search ) . '%';
		}

		$orderby = 'hits';
		$order   = 'DESC';
		if ( isset( $_GET['orderby'] ) && in_array( $_GET['orderby'], array( 'hits', 'last_seen' ), true ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$orderby = sanitize_key( wp_unslash( $_GET['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$order   = ( isset( $_GET['order'] ) && 'asc' === strtolower( sanitize_key( wp_unslash( $_GET['order'] ) ) ) ) ? 'ASC' : 'DESC'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}

		$where_sql = implode( ' AND ', $where );

		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$total = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}", $params ) );

		$offset      = ( $paged - 1 ) * $per_page;
		$args        = array_merge( $params, array( $per_page, $offset ) );
		$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE {$where_sql} ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d", $args ), ARRAY_A );
		// phpcs:enable

		$this->set_pagination_args(
			array(
				'total_items' => $total,
				'per_page'    => $per_page,
				'total_pages' => (int) ceil( $total / $per_page ),
			)
		);

		$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), 'path' );
	}

	/**
	 * Checkbox column.
	 *
	 * @param array $item Row.
	 * @return string
	 */
	public function column_cb( $item ) {
		return sprintf( '<input type="checkbox" name="log_ids[]" value="%d" />', (int) $item['id'] );
	}

	/**
	 * Path column + row actions + inline create-redirect form.
	 *
	 * @param array $item Row.
	 * @return string
	 */
	public function column_path( $item ) {
		$id  = (int) $item['id'];
		$out = '<strong><code>' . esc_html( $item['path'] ) . '</code></strong>';

		$actions = array();
		if ( 0 === (int) $item['status'] ) {
			$actions['redirect'] = sprintf( '<a href="#" class="rdz-404-action" data-action="open-create" data-id="%d">%s</a>', $id, esc_html__( 'Create redirect', 'redirectzen-301-redirect-manager' ) );
			$actions['ignore']   = sprintf( '<a href="#" class="rdz-404-action" data-action="ignore" data-id="%d">%s</a>', $id, esc_html__( 'Ignore', 'redirectzen-301-redirect-manager' ) );
		} elseif ( 1 === (int) $item['status'] ) {
			$actions['unignore'] = sprintf( '<a href="#" class="rdz-404-action" data-action="unignore" data-id="%d">%s</a>', $id, esc_html__( 'Stop ignoring', 'redirectzen-301-redirect-manager' ) );
		}
		$actions['delete'] = sprintf( '<a href="#" class="rdz-404-action rdz-delete" data-action="delete" data-id="%d">%s</a>', $id, esc_html__( 'Delete', 'redirectzen-301-redirect-manager' ) );

		$suggest = '';
		if ( rdz_can_use_premium() && class_exists( 'RDZ_Suggest' ) ) {
			$suggest = sprintf( '<button type="button" class="button rdz-suggest-btn" data-id="%d" data-path="%s">%s</button><span class="rdz-suggestions"></span>', $id, esc_attr( $item['path'] ), esc_html__( 'Suggest target', 'redirectzen-301-redirect-manager' ) );
		} else {
			$suggest = sprintf( '<a class="rdz-pro-hint" href="%s" title="%s">%s</a>', esc_url( RDZ_Admin::upgrade_url() ), esc_attr__( 'Pro fuzzy-matches this dead URL to your live content.', 'redirectzen-301-redirect-manager' ), esc_html__( 'Suggest target (Pro)', 'redirectzen-301-redirect-manager' ) );
		}

		$create_form = sprintf(
			'<div class="rdz-inline-create" data-id="%d" data-path="%s" style="display:none">
				<input type="text" class="rdz-create-target code" placeholder="%s" aria-label="%s" />
				<select class="rdz-create-type">%s</select>
				<button type="button" class="button button-primary rdz-save-create">%s</button>
				<button type="button" class="button rdz-cancel-create">%s</button>
				%s
			</div>',
			$id,
			esc_attr( $item['path'] ),
			esc_attr__( 'https://… or /new-page/', 'redirectzen-301-redirect-manager' ),
			esc_attr__( 'Redirect target', 'redirectzen-301-redirect-manager' ),
			self::type_options( (int) RDZ_Settings::get( 'default_redirect_type' ) ),
			esc_html__( 'Redirect', 'redirectzen-301-redirect-manager' ),
			esc_html__( 'Cancel', 'redirectzen-301-redirect-manager' ),
			$suggest
		);

		return $out . $this->row_actions( $actions ) . $create_form;
	}

	/**
	 * Options markup for the type select.
	 *
	 * @param int $selected Selected code.
	 * @return string
	 */
	private static function type_options( $selected ) {
		$out = '';
		foreach ( RDZ_Settings::$redirect_types as $code ) {
			$out .= sprintf( '<option value="%1$d"%2$s>%1$d</option>', (int) $code, selected( $selected, $code, false ) );
		}

		return $out;
	}

	/**
	 * Hits column.
	 *
	 * @param array $item Row.
	 * @return string
	 */
	public function column_hits( $item ) {
		return '<strong>' . esc_html( number_format_i18n( (int) $item['hits'] ) ) . '</strong>';
	}

	/**
	 * Last-seen column.
	 *
	 * @param array $item Row.
	 * @return string
	 */
	public function column_last_seen( $item ) {
		if ( empty( $item['last_seen'] ) ) {
			return '—';
		}

		/* translators: %s: human-readable time difference. */
		return esc_html( sprintf( __( '%s ago', 'redirectzen-301-redirect-manager' ), human_time_diff( strtotime( $item['last_seen'] . ' UTC' ), time() ) ) );
	}

	/**
	 * Referrer column.
	 *
	 * @param array $item Row.
	 * @return string
	 */
	public function column_referrer( $item ) {
		if ( empty( $item['referrer'] ) ) {
			return '<span class="rdz-mut">—</span>';
		}

		$host = wp_parse_url( $item['referrer'], PHP_URL_HOST );

		return sprintf( '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>', esc_url( $item['referrer'] ), esc_html( $host ? $host : $item['referrer'] ) );
	}

	/**
	 * Empty-state message.
	 */
	public function no_items() {
		esc_html_e( 'No 404s logged. That is exactly what you want to see.', 'redirectzen-301-redirect-manager' );
	}
}
