<?php

namespace KirilKirkov\WpInvoices;

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

class Invoices
{
    const DEFAULT_INV_NUM_LENGTH = 10;

    const TABLE_NAME = 'kirilkirkov_wp_invoices';
    const TABLE_CLIENTS_NAME = 'kirilkirkov_wp_clients_to_invoices';
    const TABLE_ITEMS_NAME = 'kirilkirkov_wp_items_to_invoices';

    public $table_name;
    public $table_clients_to_invoices_name;
    public $table_items_to_invoices_name;

    public function init()
    {
        global $wpdb;
        
        $this->table_name = $wpdb->prefix . self::TABLE_NAME;
        $this->table_clients_to_invoices_name = $wpdb->prefix . self::TABLE_CLIENTS_NAME;
        $this->table_items_to_invoices_name = $wpdb->prefix . self::TABLE_ITEMS_NAME;
    }

    public function __construct()
    {
        $this->init();
        // its not good to add another functions here. Its called in many places.
    }

    /**
     * Called from plugin activation hook
     */
    public static function create_tables()
    {
        $i = new self();
        $i->init(); // set tables names

        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );

        // main invoices table
        $sql = "CREATE TABLE IF NOT EXISTS ".$i->table_name." (
            `id` mediumint(9) NOT NULL AUTO_INCREMENT,
            `wc_id` mediumint(9) NULL DEFAULT NULL,
            `invoice_type` enum('pro_forma','invoice','debit_note','credit_note') NOT NULL,
            `to_invoice_number` varchar(200) DEFAULT NULL,
            `from_date` varchar(100) DEFAULT NULL,
            `invoice_number` varchar(150) DEFAULT NULL,
            `invoice_date` varchar(100) DEFAULT NULL,
            `date_tax_event` varchar(100) DEFAULT NULL,
            `due_date` varchar(100) DEFAULT NULL,
            `has_maturity` tinyint(1) NOT NULL DEFAULT '0',
            `maturity_date` varchar(100) DEFAULT NULL,
            `cash_accounting` tinyint(1) NOT NULL DEFAULT '0',
            `currency` varchar(50) DEFAULT NULL,
            `items_total` varchar(100) DEFAULT NULL,
            `discount` varchar(100) DEFAULT NULL,
            `discount_type` varchar(50) DEFAULT NULL,
            `vat_percent` varchar(100) DEFAULT NULL,
            `vat_sum` varchar(100) DEFAULT NULL,
            `no_vat` tinyint(1) NOT NULL DEFAULT '0',
            `no_vat_reason` varchar(255) DEFAULT NULL,
            `tax_base` varchar(100) DEFAULT NULL,
            `final_total` varchar(100) DEFAULT NULL,
            `remarks` text,
            `payment_method` varchar(255) DEFAULT NULL,
            `paid` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0-unpaid, 1-paid, 2-partly paid',
            `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0 - issued, 1 - send, 2 - draft',
            `send_at` varchar(100) DEFAULT NULL,
            `created_from` int(11) NOT NULL,
            `compiled` varchar(255) DEFAULT NULL,
            `schiffer` varchar(255) DEFAULT NULL,
            `customer_user_id` int(10) UNSIGNED NULL DEFAULT NULL,
            `created_at` datetime NOT NULL,
            `updated_at` datetime NOT NULL,
            UNIQUE KEY id (id),
            UNIQUE KEY invoice_type (invoice_type, invoice_number)
        ) $charset_collate;";
        dbDelta($sql);

        // clients to invoices
        $sql = "CREATE TABLE IF NOT EXISTS ".esc_sql($i->table_clients_to_invoices_name)." (
            `id` mediumint(9) NOT NULL AUTO_INCREMENT,
            `invoice_id` int(10) UNSIGNED NOT NULL,
            `client_from_list` mediumint(9) NOT NULL DEFAULT '0',
            `client` varchar(255) NOT NULL,
            `is_individual` tinyint(1) NOT NULL DEFAULT '0',
            `identification_number` varchar(255) DEFAULT NULL,
            `has_vat_registration` tinyint(1) NOT NULL DEFAULT '0',
            `vat_number` varchar(255) DEFAULT NULL,
            `accountable_person` varchar(255) DEFAULT NULL,
            `city` varchar(255) DEFAULT NULL,
            `address` varchar(300) DEFAULT NULL,
            `country` varchar(255) DEFAULT NULL,
            `beneficiary` varchar(255) DEFAULT NULL,
            `zip_code` varchar(200) DEFAULT NULL,
            `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            UNIQUE KEY id (id)
        ) $charset_collate;";
        dbDelta($sql);

        // items to invoices
        $sql = "CREATE TABLE IF NOT EXISTS ".esc_sql($i->table_items_to_invoices_name)." (
            `invoice_id` int(10) UNSIGNED NOT NULL,
            `name` varchar(255) DEFAULT NULL,
            `quantity` varchar(100) DEFAULT NULL,
            `quantity_type` varchar(150) DEFAULT NULL,
            `price` varchar(100) DEFAULT NULL,
            `total` varchar(100) DEFAULT NULL,
            `item_from_list` int(11) NOT NULL DEFAULT '0',
            `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
        ) $charset_collate;";
        dbDelta($sql);
    }

    /**
     * Add default Currencies, Quantity Types, Payment methods and invoice settings if there are no added
     */
    public static function add_default_options()
    {
        $currencies = get_option(Config::INPUTS_PREFIX.'currencies');
        if(!$currencies) {
            update_option(Config::INPUTS_PREFIX.'currencies', ['USD', 'EUR']);
        }
        
        $quantity_types = get_option(Config::INPUTS_PREFIX.'quantity_types');
        if(!$quantity_types) {
            update_option(Config::INPUTS_PREFIX.'quantity_types', ['Package', 'Kilogram']);
        }

        $payment_methods = get_option(Config::INPUTS_PREFIX.'payment_methods');
        if(!$payment_methods) {
            update_option(Config::INPUTS_PREFIX.'payment_methods', ['Bank Payment', 'Stripe', 'Paypal']);
        }
        
        $invoices_num_length = get_option(Config::INPUTS_PREFIX.'invoices_num_length');
        if(!$invoices_num_length) {
            update_option(Config::INPUTS_PREFIX.'invoices_num_length', 10);
        }

        $count_decimal_digits = get_option(Config::INPUTS_PREFIX.'count_decimal_digits');
        if(!$count_decimal_digits) {
            update_option(Config::INPUTS_PREFIX.'count_decimal_digits', 2);
        }
    }

    public static function get_next_inv_number($type)
    {
        global $wpdb;
        
        $i = new self();
        $i->init(); // set tables names

        // 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.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $max_id = $wpdb->get_var(
            $wpdb->prepare( 
                'SELECT MAX(invoice_number) FROM ' . esc_sql($i->table_name) . ' WHERE invoice_type = %s',
                $type
            )
        );
        if(!$max_id) {
            $max_id = 0;
        }
        
        if((int)get_option(Config::INPUTS_PREFIX.'disalbe_origin_inv_generator') < 1) {
            if(is_numeric($max_id)) {
                $max_id = $max_id + 1;
                return self::full_document_number($max_id);
            }
        }

        return '';
    }
    
    /**
     * Generate invoice length number
     */
    public static function full_document_number($inv, $length = null, $padding = '0')
    {
        $inv_num_length = $length;
        if($length === null) {
            $inv_num_length = self::DEFAULT_INV_NUM_LENGTH;
            $invoices_num_length = get_option( Config::INPUTS_PREFIX.'invoices_num_length' );
            if($invoices_num_length && (int)$invoices_num_length > 0) {
                $inv_num_length = (int)$invoices_num_length;
            }
        }

        return str_pad(intval($inv), intval($inv_num_length), $padding, STR_PAD_LEFT);
    }

    /**
     * Get single invoice by db autoincrement id
     * 
     * @param int $id - id of the invoice
     * @param bool $mapped - map with prefixes
     * @param bool $all_fields - get all fields from db
     * @param bool $is_public_call - is public call (do not get the periodic invoice if is periodic), default false. This is true when called from shortcode. Also prevent broking of api calls
     */
    public static function get_invoice($id, $mapped = false, $all_fields = false, $is_public_call = false)
    {
        if((int)$id < 1) {
            return false;
        }
        $id = (int)$id;
        
        global $wpdb;
        $w = new Invoices();

        $fields = 'id, invoice_type, to_invoice_number, from_date, invoice_number,
        invoice_date, date_tax_event, due_date, has_maturity, maturity_date, cash_accounting, currency, 
        items_total, discount, discount_type, vat_percent, vat_sum, no_vat, no_vat_reason, 
        final_total, remarks, payment_method, tax_base, schiffer, compiled, wc_id, customer_user_id';
        if($all_fields === true) {
            $fields = 'id, invoice_type, to_invoice_number, from_date, invoice_number,
            invoice_date, date_tax_event, due_date, has_maturity, maturity_date, cash_accounting, currency, 
            items_total, discount, discount_type, vat_percent, vat_sum, no_vat, no_vat_reason, tax_base, 
            final_total, remarks, payment_method, paid, status, send_at, created_from, compiled, schiffer, 
            created_at, updated_at, wc_id, customer_user_id';
        }

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation. $fields variable is hardcoded so no need prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
        $result = $wpdb->get_results( $wpdb->prepare( 'SELECT ' . ($fields) . ' 
        FROM ' . esc_sql($w->table_name) . ' 
        WHERE id = %d 
        LIMIT 1', 
        $id), $mapped === true ? OBJECT : ARRAY_A );
        if(!count($result)) {
            return false;
        }
        
        $invoice = $result[0];

        // Add periodic field to result, if is periodic (only if is not used in public page or api call), (true in constructor)
        if($is_public_call === false) {
            $periodicInvoices = new PeriodicInvoices(true);

            // 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.
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $periodic = $wpdb->get_var( $wpdb->prepare( 'SELECT count(id) FROM ' . esc_sql($periodicInvoices->table_name) . ' WHERE invoice_id = %d', $id) );
            if($periodic > 0) {
                if($mapped === true) {
                    $invoice->save_periodic_inv = true;
                } else {
                    $invoice['save_periodic_inv'] = true;
                }
            }
        }

        if($mapped === true) {
            $invoice = self::mapTaskWithPrefix($invoice);
        }

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $client = $wpdb->get_results( $wpdb->prepare( 'SELECT id, client_from_list, client, 
        is_individual, identification_number, has_vat_registration, vat_number, 
        accountable_person, city, address, country, beneficiary, zip_code
        FROM ' . esc_sql($w->table_clients_to_invoices_name) . ' 
        WHERE invoice_id = %d 
        LIMIT 1', 
        $id), ARRAY_A);

        $_client = [];
        if(count($client)) {
            $_client = $client[0];
            unset($client);
        }
        
        if($mapped === true) {
            $invoice[Config::INPUTS_PREFIX . 'client'] = $_client;
        } else {
            $invoice['client'] = $_client;
        }

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $items = $wpdb->get_results( $wpdb->prepare( 'SELECT name, quantity,
        quantity_type, price, total, item_from_list
        FROM ' . esc_sql($w->table_items_to_invoices_name) . ' 
        WHERE invoice_id = %d', 
        $id), OBJECT);
        $_items = [];

        if(count($items)) {
            foreach($items as $item) {
                $_items['item_from_list'][] = $item->item_from_list;
                $_items['items_names'][] = $item->name;
                $_items['items_quantities'][] = $item->quantity;
                $_items['items_quantity_types'][] = $item->quantity_type;
                $_items['items_prices'][] = $item->price;
                $_items['items_totals'][] = $item->total;
            }
        }
        
        if($mapped === true) {
            $invoice[Config::INPUTS_PREFIX . 'items'] = $_items;
        } else {
            $invoice['items'] = $_items;
        }
        
        return $invoice; 
    }

    /**
     * Return invoice by slug or false if not found
     */
    public static function get_invoice_by_slug($slug)
    {
        global $wpdb;
        
        $d = new InvoicesAData();

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation. The table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM " . esc_sql($d->table_name) . " WHERE slug = %s LIMIT 1", $slug), ARRAY_A);
        if (count($results) < 1) {
            return false;
        }

        $invoice_id = $results[0]['invoice_id'];
        return self::get_invoice($invoice_id, false, true, true); // get with all fields
    }

    /**
     * Get all invoices
     * 
     * @param bool $mapped - map with prefixes
     */
    public static function get_invoices($mapped = false)
    {        
        global $wpdb;
        $w = new Invoices();

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation. The table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $results = $wpdb->get_results( $wpdb->prepare( 'SELECT id FROM ' . esc_sql($w->table_name)), ARRAY_A );
        if(!count($results)) {
            return false;
        }
        
        $invoices = array();
        foreach($results as $result) {
            $invoices[] = self::get_invoice($result['id'], $mapped, true);
        }

        return $invoices;
    }

    private static function mapTaskWithPrefix($article)
    {
        $prefixed = [];
        foreach($article as $k => $v) {
            if($k === 'id') {
                $prefixed['id'] = $v;
                continue;
            }
            $prefixed[Config::INPUTS_PREFIX . $k] = $v;
        }
        
        return $prefixed;
    }

    public static function get_customer_invoices($customer_id)
    {
        global $wpdb;
        $w = new Invoices();

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation. The table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $results = $wpdb->get_results( $wpdb->prepare( 'SELECT id, invoice_number, created_at FROM ' . esc_sql($w->table_name) . ' WHERE customer_user_id = %d', $customer_id), ARRAY_A ); 

        if(count($results) < 1) {
            return [];
        }

        $data = [];
        foreach($results as $result) {
            $result['created_at'] = strtotime($result['created_at']);
            $data[] = $result;
        }

        return $data;
    }

    /**
     * Get all items for invoice
     * 
     * @param int $invoice_id - Id of an invoice
     */
    public static function get_invoice_items($invoice_id)
    {
        global $wpdb;
        $w = new Invoices();

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation. The table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->get_results( $wpdb->prepare( 'SELECT item_from_list, quantity  
        FROM ' . esc_sql($w->table_items_to_invoices_name) . 
        ' WHERE invoice_id = %d', $invoice_id), OBJECT ); 
        if(!count($result)) {
            return [];
        }

        return $result;
    }

    public static function mark_invoice_as_sended($invoice_id)
    {
        global $wpdb;
        $w = new Invoices();

        $dt = new \DateTime();
        $today_date = $dt->format(Config::get_dates_format_type());

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation. The table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . esc_sql($w->table_name) . ' 
        SET status = 1, send_at = %s WHERE id = %d LIMIT 1', $today_date, $invoice_id ) 
        , OBJECT );
    }

    /**
     * Change invoice payment status
     */
    public static function change_inv_paid_status($id, $status)
    {
        $st = Config::get_invoice_paid_types();
        if(!isset($st[$status])) {
            return;
        }

        global $wpdb;
        $w = new Invoices();

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() with prepare() – the standard and safe approach for this operation. The table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . esc_sql($w->table_name) . ' 
        SET paid = %d WHERE id = %d 
        LIMIT 1', 
        $status,
        $id
        ) 
        , OBJECT );
    }

    /**
     * Preview created invoice
     * Download as pdf
     */
    public static function preview_inv_page()
    {
        /**
         * No nonce verification needed here – just checking the view parameter for previewing invoice
         * there is no modification action performed. Only data retrieval and display.
         */

        // No nonce verification needed here – just getting the invoice by id for previewing
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if(!isset($_GET['view']) || (int)$_GET['view'] < 1) {
            require plugin_dir_path( __FILE__ ) . '../../Views/404.php';
            return;
        }

        // No nonce verification needed here – just getting the invoice by id for previewing
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $invoice = Invoices::get_invoice((int)$_GET['view']);
        if($invoice === false) {
            require plugin_dir_path( __FILE__ ) . '../../Views/404.php';
            return;
        }
        $invoice_adata = InvoicesAData::get_adata($invoice['id']);

        $Config = Config::class;
        $inv_dwn_types = Config::get_inv_dwn_types();
        $inv_dwn_type = $inv_dwn_types['original'];

        require plugin_dir_path( __FILE__ ) . '../../Views/Invoices/Preview.php';
    }

    /**
     * Send created invoice
     * Send as pdf by email
     */
    public static function send_inv_page()
    {
        if(!isset($_GET['id']) || (int)$_GET['id'] < 1) {
            require plugin_dir_path( __FILE__ ) . '../../Views/404.php';
            return;
        }

        // No nonce verification needed here – just getting the invoice by id for sending
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $invoice = Invoices::get_invoice((int)$_GET['id']);
        if($invoice === false) {
            require plugin_dir_path( __FILE__ ) . '../../Views/404.php';
            return;
        }

        $success = null;

        // if submit form
        if(
            isset( $_SERVER['REQUEST_METHOD'] ) && 
            $_SERVER['REQUEST_METHOD'] === 'POST' &&
            isset($_POST[Config::INPUTS_PREFIX.'send_invoice_emails']) &&
            isset($_POST['send_invoice_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['send_invoice_nonce'])), 'send_invoice_nonce')
            ) {

            if(SendInvoice::send($_POST, $invoice) === false) {
                $errors = SendInvoice::get_validation_errors();
                $success = false;
            } else {
                $success = true;
            }
        }

        if($success === false) {
            $client = array_map('sanitize_text_field', wp_unslash($_POST));
        }
        $Config = Config::class;

        require plugin_dir_path( __FILE__ ) . '../../Views/Invoices/Send.php';
    }

    /**
     * Handle get param for inv download
     */
    public static function handle_download_inv()
    {
        /**
         * No nonce verification needed here – just checking the get parameters for downloading invoice.
         * This checks are to know that the user is into the correct page for downloading invoice.
         * there is no modification action performed. Only data retrieval and display.
         */

        // No nonce verification needed here – just checking the "page" parameter for downloading invoice
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if(!isset($_GET['page']) || $_GET['page'] !== Config::SUB_MENU_PREVIEW_INVOICE_PAGE_SLUG) {
            return;
        }

        // No nonce verification needed here – just checking the "download-invoice" parameter for downloading invoice
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if(!isset($_GET['download-invoice']) || (!isset($_GET['page']) || $_GET['page'] !== Config::SUB_MENU_PREVIEW_INVOICE_PAGE_SLUG)) {
            return;
        }

        /* 
         * #### HERE WE HAVE NONCE FOR DOWNLOAD INVOICE AS REVIEWER CHECK WANTED. ####
         * 
         * ONLY IF WE ARE INTO THE DOWNLOAD PAGE (ALL THE ABOVE CHECKS JUST VERIFY THAT WE ARE IN THIS PAGE)
         */ 
        if ( ! isset($_GET['view'], $_GET['nonce_download_invoice']) ) {
            wp_die(
                esc_html__('Security check failed. Please try again.', 'kirilkirkov-pdf-invoice-manager')
            );
        }
        $invoice_id = absint( $_GET['view'] );
        $nonce = sanitize_text_field( wp_unslash( $_GET['nonce_download_invoice'] ) );
        if ( ! wp_verify_nonce( $nonce, 'download_invoice_' . $invoice_id ) ) {
            wp_die(
                esc_html__('Security check failed. Please try again.', 'kirilkirkov-pdf-invoice-manager')
            );
        }

        // No nonce verification needed here – just checking the "view" parameter for downloading invoice
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if(!isset($_GET['view']) || (int)$_GET['view'] < 1) {
            require plugin_dir_path( __FILE__ ) . '../../Views/404.php';
            return;
        }

        // No nonce verification needed here – just checking the "view" parameter for downloading invoice
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $invoice = Invoices::get_invoice((int)$_GET['view']);
        if($invoice === false) {
            require plugin_dir_path( __FILE__ ) . '../../Views/404.php';
            return;
        }

        $inv_dwn_types = Config::get_inv_dwn_types();

        // No nonce verification needed here – just checking the "download-invoice" parameter for downloading invoice
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $inv_dwn_type = isset($inv_dwn_types[$_GET['download-invoice']]) ? $inv_dwn_types[sanitize_text_field(wp_unslash($_GET['download-invoice']))] : $inv_dwn_types['original'];
        $Config = Config::class;
        
        ob_start();
        require_once plugin_dir_path( __FILE__ ) . '../../Views/Invoices/Templates/download_invoice.php';
        $html = ob_get_clean();

        if(!class_exists('\Dompdf\Dompdf')) {
            require_once plugin_dir_path( __FILE__ ) . '../../../../vendor/autoload.php';
        }
        $dompdf = new \Dompdf\Dompdf();

        $dompdf->loadHtml($html);

        // (Optional) Setup the paper size and orientation
        // [0, 0, 867.00, 1008.00]
        $dompdf->setPaper('A4', 'portrait');

        // Render the HTML as PDF
        $dompdf->render();
        
        // Output the generated PDF to Browser
        $dompdf->stream($invoice['invoice_number']);
        die();
    }

    /**
     * @param string $order - ASC/DESC
     * its used to order by FIELD(id,...) in invoices list table/page
     */
    public static function get_ordered_invoices_by_client($order)
    {
        global $wpdb;
        $w = new Invoices();

        $ord = strtolower($order) === 'asc' ? 'ASC' : 'DESC';

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() - the standard and safe approach for this operation. The table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->get_results("SELECT invoice_id
        FROM " . esc_sql($w->table_clients_to_invoices_name) . " 
        ORDER BY client " . esc_sql($ord), OBJECT );
        if(!$result || !count($result)) {
            return array();
        }

        $ids = array();
        foreach($result as $r) {
            $ids[] = $r->invoice_id;
        }
        return $ids;
    }

    public static function get_all_clients_to_invoices()
    {
        global $wpdb;
        $w = new Invoices();

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() - the standard and safe approach for this operation. Table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->get_results("SELECT DISTINCT client 
        FROM " . esc_sql($w->table_clients_to_invoices_name), OBJECT ); 
        if(!$result || !count($result)) {
            return array();
        }

        return $result;
    }

    public static function get_all_items_to_invoices()
    {
        global $wpdb;
        $w = new Invoices();

        // We need a fresh result directly from the database (not cached). Using $wpdb->get_results() - the standard and safe approach for this operation. Table name is hardcoded so no need to prepare..
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->get_results("SELECT DISTINCT name 
        FROM " . esc_sql($w->table_items_to_invoices_name), OBJECT ); 
        if(!$result || !count($result)) {
            return array();
        }

        return $result;
    }
}