<?php

namespace KirilKirkov\WpInvoices;

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

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

/**
 * Invoices table
 */
class InvoicesList extends \WP_List_Table
{
    private $table_name;
    private $table_clients_to_invoices_name;
    private $table_items_to_invoices_name;

    private $clients = [];

    public function __construct()
    {
        parent::__construct();
        
        $i = new Invoices();
        $i->init(); // set tables names

        $this->table_name = $i->table_name;
        $this->table_clients_to_invoices_name = $i->table_clients_to_invoices_name;
        $this->table_items_to_invoices_name = $i->table_items_to_invoices_name;
    }

    /**
     * Invoices list admin page
     */
    public function list_invoices_page()
    {
        $table = $this;

        $Config = Config::class;
        
        require plugin_dir_path( __FILE__ ) . '../../Views/Invoices/InvoicesList.php';
    }

    /**
     * Screen options invoices list page
     */
    public function screen_option_invoices()
    {
        global $kirilkirkovwipdf_invoices_list_per_page;
 
        $screen = get_current_screen();
    
        // get out of here if we are not on our settings page
        if(!is_object($screen) || $screen->id != $kirilkirkovwipdf_invoices_list_per_page) {
            return;
        }

        add_screen_option( 'per_page', array(
            'label'   => esc_html__('Invoices per page', 'kirilkirkov-pdf-invoice-manager'),
            'default' => 10,
            'option'  => 'kirilkirkovwipdf_invoices_list_per_page'
        ) );
    }

    public function get_columns()
    {
        $columns = array(
          'cb' => '<input type="checkbox" />',
          'id' => esc_html__('Invoice ID', 'kirilkirkov-pdf-invoice-manager'),
          'invoice_number' => esc_html__('Number', 'kirilkirkov-pdf-invoice-manager'),
          'invoice_date' => esc_html__('Date', 'kirilkirkov-pdf-invoice-manager'),
          'client' => esc_html__('Client', 'kirilkirkov-pdf-invoice-manager'),
          'invoice_type' => esc_html__('Type', 'kirilkirkov-pdf-invoice-manager'),
          'paid' => esc_html__('Payments', 'kirilkirkov-pdf-invoice-manager'),
          'status' => esc_html__('Status', 'kirilkirkov-pdf-invoice-manager'),
          'send_at' => esc_html__('Sended At', 'kirilkirkov-pdf-invoice-manager'),
          'final_total' => esc_html__('Total Amount', 'kirilkirkov-pdf-invoice-manager'),
        );
        return $columns;
    }
      
    public function column_cb($item)
    {
        return sprintf(
            '<input type="checkbox" name="delete[]" value="%s" />', 
            $item['id']
        );
    }

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array(
            'id' => array('id', false),
            'invoice_type' => array('invoice_type', false),
            'client' => array('client', false),
            'final_total' => array('final_total', false),
            'status' => array('status', false),
            'paid' => array('paid', false),
            'invoice_number' => array('invoice_number', false),
            'invoice_date' => array('invoice_date', false),
            'send_at' => array('send_at', true),
        );
        $this->_column_headers = array($columns, $hidden, $sortable);

        /** Process bulk action */
		$this->process_bulk_action();

        // fetch data and set pagination
        $per_page  = $this->get_items_per_page( 'kirilkirkovwipdf_invoices_list_per_page', 10);

        $current_page = $this->get_pagenum();
		$total_items  = $this->record_count();
        $this->set_pagination_args([
			'total_items' => $total_items, //WE have to calculate the total number of items
			'per_page'  => $per_page, //WE have to determine how many items to show on a page,
            'total_pages' => ceil( $total_items / $per_page ) // use ceil to round up
		]);

        $table_data = $this->fetch_table_data($per_page, $current_page);
        $this->items = $table_data;
    }

    public function process_bulk_action()
    {
		// If the delete bulk action is triggered
		if ( ( isset( $_POST['action'] ) && $_POST['action'] == 'delete' )
		     || ( isset( $_POST['action2'] ) && $_POST['action2'] == 'delete' )) {

            if(!isset($_POST['bulk_invoices_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['bulk_invoices_nonce'])), 'bulk_invoices_nonce')) {
                wp_die( esc_html__('Invalid nonce', 'kirilkirkov-pdf-invoice-manager') );
            }
			$delete_ids = isset($_POST['delete']) ? array_map( 'absint',wp_unslash($_POST['delete'])) : [];
            if(is_array($delete_ids) && count($delete_ids)) {
                foreach ( $delete_ids as $id ) {
                    $this->delete_record( $id );
                }
            }
		}
	}

    /**
	 * Delete record.
	 *
	 * @param int $id - ID
	 */
	public function delete_record($id)
    {
		global $wpdb;
        $id = (int)$id;
        if($id < 1) {
            return;
        }

        /**
         * Delete a record from the custom table.
         * We use $wpdb->delete() – the recommended way to perform DELETE operations on custom tables.
         * Caching is not applicable for deletion operations.
         */
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->delete($this->table_name, array( 'id' => $id ), array( '%d' ) );
        InvoicesAData::delete_adata($id);

        // items
        /**
         * Delete a record from the custom table.
         * We use $wpdb->delete() – the recommended way to perform DELETE operations on custom tables.
         * Caching is not applicable for deletion operations.
         */
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->delete($this->table_clients_to_invoices_name, array( 'invoice_id' => $id ), array( '%d' ) );
        
        // client
        /**
         * Delete a record from the custom table.
         * We use $wpdb->delete() – the recommended way to perform DELETE operations on custom tables.
         * Caching is not applicable for deletion operations.
         */
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->delete($this->table_items_to_invoices_name, array( 'invoice_id' => $id ), array( '%d' ) );
	}

    private function get_query_where()
    {
        global $wpdb;
        
        $inv = new Invoices();

        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $search = ( isset( $_REQUEST['s'] ) ) ? sanitize_text_field(wp_unslash($_REQUEST['s'])) : false;
        
        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $paid_s = ( isset( $_REQUEST['paid_s'] ) && $_REQUEST['paid_s'] !== 'none' ) ? (int)$_REQUEST['paid_s'] : 'none';
        
        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $status_s = ( isset( $_REQUEST['status_s'] ) && $_REQUEST['status_s'] !== 'none' ) ? (int)$_REQUEST['status_s'] : 'none';
        
        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $type_s = ( isset( $_REQUEST['type_s'] ) && $_REQUEST['type_s'] !== 'none' ) ? sanitize_text_field(wp_unslash($_REQUEST['type_s'])) : 'none';
        
        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $client_s = ( isset( $_REQUEST['client_s'] ) && $_REQUEST['client_s'] !== 'none' ) ? sanitize_text_field(wp_unslash($_REQUEST['client_s'])) : 'none';
        
        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $item_s = ( isset( $_REQUEST['item_s'] ) && $_REQUEST['item_s'] !== 'none' ) ? sanitize_text_field(wp_unslash($_REQUEST['item_s'])) : 'none';
        
        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $invoice_date_s = ( isset( $_REQUEST['invoice_date_s'] ) && $_REQUEST['invoice_date_s'] !== '' ) ? sanitize_text_field(wp_unslash($_REQUEST['invoice_date_s'])) : 'none';
        
        // No nonce verification needed here – only reading safe GET params for filtering.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $wc_id_s = ( isset( $_REQUEST['wc_id_s'] ) && $_REQUEST['wc_id_s'] !== '' ) ? sanitize_text_field(wp_unslash($_REQUEST['wc_id_s'])) : 'none';

        $where = [];
        if($search && trim($search) != '') {
            $where[] = $wpdb->prepare( ' (invoice_number LIKE %s) ', "%$search%");
        }
        if($paid_s !== 'none') {
            $where[] = $wpdb->prepare( " (paid = %d) ", (int)$paid_s);
        }
        if($status_s !== 'none') {
            $where[] = $wpdb->prepare( " (status = %d) ", (int)$status_s);
        }
        if($type_s !== 'none') {
            $where[] = $wpdb->prepare( " (invoice_type = %s) ", (string)$type_s);
        }
        if($client_s !== 'none') {
            $where[] = $wpdb->prepare( " (" . esc_sql($inv->table_name) . ".id IN (SELECT invoice_id FROM " . esc_sql($inv->table_clients_to_invoices_name) . " WHERE " . esc_sql($inv->table_clients_to_invoices_name) . ".client = %s)) ", (string)$client_s);
        }
        if($item_s !== 'none') {
            $where[] = $wpdb->prepare( " (" . esc_sql($inv->table_name) . ".id IN (SELECT invoice_id FROM " . esc_sql($inv->table_items_to_invoices_name) . " WHERE " . esc_sql($inv->table_items_to_invoices_name) . ".name = %s)) ", (string)$item_s);
        }
        if($invoice_date_s !== 'none') {
            $invoice_date_format_sql = Config::get_date_format_in_sql();
            $invoice_date_format_php = Config::get_dates_format_type();

            if($invoice_date_format_sql !== null) {
                $datetime = \DateTime::createFromFormat($invoice_date_format_php, $invoice_date_s);
                if($datetime) {
                    $where[] = $wpdb->prepare( " (STR_TO_DATE(invoice_date, '" . esc_sql((string)$invoice_date_format_sql) . "') >= %s) ", (string)$datetime->format('Y-m-d'));
                }
            }
        }
        if($wc_id_s !== 'none') {
            $where[] = $wpdb->prepare( " (wc_id = %s) ", (string)$wc_id_s);
        }
        
        return $where;
    }

    /**
	 * Returns the count of records in the database.
	 *
	 * @return null|string
	 */
	private function record_count()
    {
		global $wpdb;

        $q = "SELECT COUNT(id) FROM {$this->table_name}";
        $where = $this->get_query_where();
        if(count($where)) {
            $q .= ' WHERE '.join(' AND ', $where);
        }

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_var() with prepare() – the standard and safe approach for this operation.
        // All the $where parts are already in prepared statement and escaped in $this->get_query_where().
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
		return $wpdb->get_var($q);
	}

    private function fetch_table_data($per_page = 5, $page_number = 1)
    {
        global $wpdb;

        // No nonce verification needed here – only reading a safe GET param for search.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $orderby = $orderby_raw = ( isset( $_GET['orderby'] ) ) ? esc_sql( sanitize_text_field( wp_unslash($_GET['orderby']) ) ) : 'id';
        
        // No nonce verification needed here – only reading a safe GET param for search.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $order = ( isset( $_GET['order'] ) ) ? esc_sql( sanitize_text_field( wp_unslash($_GET['order']) ) ) : 'ASC';
        
        $orderby = $this->sanitize_orderby( $orderby, $order );
        $order = $this->sanitize_order_type( $order, $orderby_raw );
        $offset = ( $page_number - 1 ) * $per_page;

        $user_query = "SELECT id, invoice_number, invoice_date, 
        invoice_type, paid, status, send_at, final_total FROM " . esc_sql($this->table_name);

        $where = $this->get_query_where();
        if(count($where)) {
            $user_query .= ' WHERE '.join(' AND ', $where);
        }

        // $orderby and $order are sanitized with ->sanitize_orderby and ->sanitize_order_type
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $user_query .= $wpdb->prepare( " ORDER BY " . esc_sql($orderby) . " " . esc_sql($order) . " LIMIT %d OFFSET %d", $per_page, $offset);

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() – the standard and safe approach for this operation.
        // All the $where parts are already prepared in $this->get_query_where().
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
        return $wpdb->get_results( $user_query, ARRAY_A  );
    }

    public function extra_tablenav( $which )
    {
        if ( $which == "top" ) {
            $this->add_dropdown_paid_status();
            $this->add_dropdown_status();
            $this->add_dropdown_type();
            $this->add_dropdown_client();
            $this->add_dropdown_item();
            $this->add_input_wc_id();
            submit_button( esc_html__( 'Filter', 'kirilkirkov-pdf-invoice-manager' ),
				'secondary', false, false, array( 'id' => 'post-query-submit' ) );
         }
    }

    /**
     * Add dropdown to the table for categories filter
     */
    private function add_dropdown_paid_status()
    {
        $types = Config::get_invoice_paid_types();
    ?>
		<label for="filter-by-paid-type" class="screen-reader-text"><?php esc_html_e('Filter by paid status', 'kirilkirkov-pdf-invoice-manager');?></label>
		<select name="paid_s" id="filter-by-paid-type">
			<option value="none"><?php esc_html_e('All Payments', 'kirilkirkov-pdf-invoice-manager');?></option>
            <?php if($types) {
                foreach($types as $t => $tt) {
                    // No nonce verification needed here – just checking the paid_s for selection
                    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
                    $paid_s = ( isset( $_REQUEST['paid_s'] ) && $_REQUEST['paid_s'] !== 'none' ) ? (int)$_REQUEST['paid_s'] : 'none';
                    ?>
                    <option <?php selected($paid_s, (int)$t); ?> value="<?php echo esc_attr(sanitize_text_field(wp_unslash($t))); ?>"><?php echo esc_attr(sanitize_text_field(wp_unslash($tt))); ?></option>
                    <?php
                }
            } ?>
		</select>
		<?php
    }

    private function add_dropdown_status()
    {
        $statuses = Config::get_invoice_statuses();
        ?>
        <label for="filter-by-status-type" class="screen-reader-text"><?php esc_html_e('Filter by invoice status', 'kirilkirkov-pdf-invoice-manager');?></label>
        <select name="status_s" id="filter-by-status-type">
            <option value="none"><?php esc_html_e('All Statuses', 'kirilkirkov-pdf-invoice-manager');?></option>
            <?php if($statuses) {
                foreach($statuses as $t => $tt) {
                    // No nonce verification needed here – just checking the status_s for selection
                    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
                    $status_s = ( isset( $_REQUEST['status_s'] ) && $_REQUEST['status_s'] !== 'none' ) ? (int)$_REQUEST['status_s'] : 'none';
                    ?>
                    <option <?php selected($status_s, (int)$t); ?> value="<?php echo esc_attr(sanitize_text_field(wp_unslash($t))); ?>"><?php echo esc_attr(sanitize_text_field(wp_unslash($tt))); ?></option>
                    <?php
                }
            } ?>
        </select>
        <?php
    }

    private function add_dropdown_type()
    {
        $types = Config::get_invoice_types();
        ?>
        <label for="filter-by-status-type-inv" class="screen-reader-text"><?php esc_html_e('Filter by invoice type', 'kirilkirkov-pdf-invoice-manager');?></label>
        <select name="type_s" id="filter-by-status-type-inv">
            <option value="none"><?php esc_html_e('All Types', 'kirilkirkov-pdf-invoice-manager');?></option>
            <?php if($types) {
                foreach($types as $t => $tt) {
                    // No nonce verification needed here – just checking the type_s for selection
                    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
                    $type_s = ( isset( $_REQUEST['type_s'] ) && $_REQUEST['type_s'] !== 'none' ) ? (string)sanitize_text_field(wp_unslash($_REQUEST['type_s'])) : 'none';
                    ?>
                    <option <?php selected($type_s, (string)$t); ?> value="<?php echo esc_attr(sanitize_text_field(wp_unslash($t))); ?>"><?php echo esc_attr(sanitize_text_field(wp_unslash($tt))); ?></option>
                    <?php
                }
            } ?>
        </select>
        <?php
    }

    private function add_dropdown_client()
    {
        $clients = Invoices::get_all_clients_to_invoices();
        ?>
        <label for="filter-by-client-inv" class="screen-reader-text"><?php esc_html_e('Filter by client', 'kirilkirkov-pdf-invoice-manager');?></label>
        <select name="client_s" class="kkinv-select2-dropdown" id="filter-by-client-inv">
            <option value="none"><?php esc_html_e('All Clients', 'kirilkirkov-pdf-invoice-manager');?></option>
            <?php if($clients && is_array($clients) && count($clients)) {
                foreach($clients as $client) {
                    // No nonce verification needed here – just checking the client_s for selection
                    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
                    $client_s = ( isset( $_REQUEST['client_s'] ) && $_REQUEST['client_s'] !== 'none' ) ? sanitize_text_field(wp_unslash($_REQUEST['client_s'])) : 'none';
                    ?>
                    <option <?php selected($client_s, $client->client); ?> value="<?php echo esc_attr(sanitize_text_field(wp_unslash($client->client))); ?>"><?php echo esc_attr(sanitize_text_field(wp_unslash($client->client))); ?></option>
                    <?php
                }
            } ?>
        </select>
        <?php
    }

    private function add_dropdown_item()
    {
        $items = Invoices::get_all_items_to_invoices();
        ?>
        <label for="filter-by-item-inv" class="screen-reader-text"><?php esc_html_e('Filter by item', 'kirilkirkov-pdf-invoice-manager');?></label>
        <select name="item_s" class="kkinv-select2-dropdown" id="filter-by-item-inv">
            <option value="none"><?php esc_html_e('All Items', 'kirilkirkov-pdf-invoice-manager');?></option>
            <?php if($items && is_array($items) && count($items)) {
                foreach($items as $item) {
                    // No nonce verification needed here – just checking the item_s for selection
                    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
                    $item_s = ( isset( $_REQUEST['item_s'] ) && $_REQUEST['item_s'] !== 'none' ) ? sanitize_text_field(wp_unslash((string)$_REQUEST['item_s'])) : 'none';
                    ?>
                    <option <?php selected($item_s, $item->name); ?> value="<?php echo esc_attr(sanitize_text_field(wp_unslash($item->name))); ?>"><?php echo esc_attr(sanitize_text_field(wp_unslash($item->name))); ?></option>
                    <?php
                }
            } ?>
        </select>
        <?php
    }

    private function add_input_wc_id()
    {
        // No nonce verification needed here – only reading the wc_id_s for filtering
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $wc_id_s = ( isset( $_REQUEST['wc_id_s'] ) ) ? sanitize_text_field(wp_unslash($_REQUEST['wc_id_s'])) : '';
        ?>
        <label for="filter-by-wc-order-id" class="screen-reader-text"><?php esc_html_e('Filter by WC Order ID', 'kirilkirkov-pdf-invoice-manager');?></label>
        <input type="text" name="wc_id_s" id="filter-by-wc-order-id" value="<?php echo esc_attr(sanitize_text_field($wc_id_s)); ?>" placeholder="<?php esc_attr_e('WC Order ID', 'kirilkirkov-pdf-invoice-manager'); ?>">
        <?php
    }

    /**
	 * Heavily restricts the possible columns by which a user can order by the table
	 *
	 * @param string $orderby - The column by which we want to order.
     * @param string|null $order - Used only for client type order because has relation table
	 *
	 * @return string
	 */
	protected function sanitize_orderby( $orderby, $order = null ) {
		$valid_column_names = [
            'id',
            'invoice_number',
            'invoice_date',
            'invoice_type',
            'status',
            'paid',
            'send_at',
            'final_total',
            'client',
		];

		if ( in_array( strtolower($orderby), $valid_column_names, true ) ) {
            if(strtolower($orderby) === 'invoice_date' || strtolower($orderby) === 'send_at') {
                $date_to_sql = Config::get_date_format_in_sql();
                if($date_to_sql !== null) {
                    return "STR_TO_DATE(" . strtolower($orderby) . ", '{$date_to_sql}')";
                }
            }
            if(strtolower($orderby) === 'client') {
                $ids = Invoices::get_ordered_invoices_by_client($order);
                if(!count($ids)) {
                    return 'id';
                }
                $imp = implode(',', $ids);
                return "FIELD(id, {$imp})";
            }

			return strtolower($orderby);
		}

		return 'created_at';
	}

    /**
	 * Heavily restricts the possible type by which a user can order
	 *
	 * @param string $order_type - The type by which we want to order asc/desc.
     * @param string $order_by - If is client type because the relation return empty
	 *
	 * @return string
	 */
	protected function sanitize_order_type( $order_type, $order_by )
    {
        if(trim(strtolower($order_by)) === 'client') {
            return '';
        }

		$valid_types = [
			'ASC',
			'DESC',
		];

		if ( in_array( strtoupper($order_type), $valid_types, true ) ) {
			return strtoupper($order_type);
		}

		return 'ASC';
	}

    public function get_bulk_actions()
    {
        $actions = array(
          'delete' => esc_html__('Delete', 'kirilkirkov-pdf-invoice-manager')
        );
        return $actions;
    }

    // WordPress looks for methods called column_{key_name}, e.g. function column_booktitle
    // this is default
    public function column_default( $item, $column_name )
    {
        return $item[$column_name];
    }

    public function column_id($item)
    {
        $edit_link = admin_url( 'admin.php?page=' . Config::SUB_MENU_CREATE_INVOICE_PAGE_SLUG . '&edit=' . $item['id'] );
        $view_link = admin_url( 'admin.php?page=' . Config::SUB_MENU_PREVIEW_INVOICE_PAGE_SLUG . '&view=' . $item['id'] );
        $output = sprintf(
            '<div class="mobile-inv-number-table-preview"><span class="kk-inv-label-mobile">%s</span><span class="kk-inv-label-desktop">%d</span></div>', 
            $item['invoice_number'], $item['id']
        );

        // Get actions.
        $actions = array(
            'edit'   => '<a href="' . esc_url( $edit_link ) . '">' . esc_html__( 'Edit', 'kirilkirkov-pdf-invoice-manager' ) . '</a>',
            'view'   => '<a href="' . esc_url( $view_link ) . '">' . esc_html__( 'View', 'kirilkirkov-pdf-invoice-manager' ) . '</a>',
        );

        $row_actions = array();

        foreach ( $actions as $action => $link ) {
            $row_actions[] = '<span class="' . esc_attr( sanitize_text_field($action) ) . '">' . $link . '</span>';
        }

        $output .= '<div>' . implode( ' ', $row_actions ) . '</div>';

        return $output;
    }

    public function column_client( $item )
    {
        if(isset($this->clients[$item['id']])) {
            return $this->clients[$item['id']];
        }

        global $wpdb;

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_var() and ->prepare() – the standard and safe approach for this operation.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->get_var( $wpdb->prepare( 'SELECT client
        FROM ' . esc_sql($this->table_clients_to_invoices_name) . ' 
        WHERE invoice_id = %d 
        LIMIT 1', 
        $item['id']) );
        if(!$result) {
            return esc_html__( 'Undefined', 'kirilkirkov-pdf-invoice-manager' );
        }

        return $result;
    }

    public function column_invoice_type( $item )
    {
        $types = Config::get_invoice_types();
        
        if(isset($types[$item['invoice_type']])) {
            return $types[$item['invoice_type']];
        }

        return esc_html__( 'Undefined', 'kirilkirkov-pdf-invoice-manager' );
    }

    public function column_paid( $item )
    {
        $types = Config::get_invoice_paid_types();

        $output = '<select class="change-inv-paid-status" data-inv-id="' . $item['id'] . '">';
        foreach($types as $k => $v) {
            $selected = '';
            if((int)$k === (int)$item['paid']) {
                $selected = 'selected="selected"';
            }
            $output .= '<option '.$selected.' value="'.(int)$k.'">'.esc_html(sanitize_text_field($v)).'</option>';
        }
        $output .= '</select>';

        return $output;
    }

    public function column_status( $item )
    {
        $s = Config::get_invoice_statuses();
        
        if(isset($s[$item['status']])) {
            return $s[$item['status']];
        }

        return esc_html__( 'Undefined', 'kirilkirkov-pdf-invoice-manager' );
    }

    public function column_send_at( $item )
    {
        if(!isset($item['send_at']) || trim($item['send_at']) === '') {
            return '-';
        }
        return trim($item['send_at']);
    }

    public function column_invoice_number( $item )
    {
        $view_link = admin_url( 'admin.php?page=' . Config::SUB_MENU_PREVIEW_INVOICE_PAGE_SLUG . '&view=' . $item['id'] );
        return '<a href="' . esc_url( $view_link ) . '">' . $item['invoice_number'] . '</a>';
    }

    /**
     * Rewrite default search_box function
     */
    public function search_box( $text, $input_id )
    {
        // No nonce verification needed here – only reading a  _REQUEST param for search.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
			return;
		}

		$input_id = $input_id . '-search-input';

        // No nonce verification needed here – only reading a  _REQUEST param for search.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! empty( $_REQUEST['orderby'] ) ) {
            // No nonce verification needed here – only reading a  _REQUEST param for search.
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			echo '<input type="hidden" name="orderby" value="' . esc_attr( sanitize_text_field(wp_unslash($_REQUEST['orderby']) )) . '" />';
		}
        
        // No nonce verification needed here – only reading a  _REQUEST param for search.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! empty( $_REQUEST['order'] ) ) {
            // No nonce verification needed here – only reading a  _REQUEST param for search.
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			echo '<input type="hidden" name="order" value="' . esc_attr( sanitize_text_field(wp_unslash($_REQUEST['order']) )) . '" />';
		}

        // No nonce verification needed here – only reading a  _REQUEST param for search.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! empty( $_REQUEST['post_mime_type'] ) ) {
            // No nonce verification needed here – only reading a _REQUEST param for search.
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( sanitize_text_field(wp_unslash($_REQUEST['post_mime_type']) )) . '" />';
		}

        // No nonce verification needed here – only reading a _REQUEST param for search.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! empty( $_REQUEST['detached'] ) ) {
            // No nonce verification needed here – only reading a _REQUEST param for search.
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			echo '<input type="hidden" name="detached" value="' . esc_attr( sanitize_text_field(wp_unslash($_REQUEST['detached']) )) . '" />';
		}
		?>
            <p class="search-box">
                <label class="screen-reader-text" for="<?php echo esc_attr( sanitize_text_field($input_id) ); ?>"><?php echo esc_html(sanitize_text_field($text)); ?>:</label>
                <input placeholder="<?php echo esc_attr__('Invoice number', 'kirilkirkov-pdf-invoice-manager') ?>" type="search" id="<?php echo esc_attr( sanitize_text_field($input_id) ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
                    <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
            </p>
		<?php
	}

}