<?php

namespace EventEspresso\core\services\database;

use EE_Base;
use EEH_Activation;
use wpdb;

/**
 * Class TableAnalysis
 * For analyzing database tables; should not perform any manipulation, or have
 * any EE business logic
 *
 * @package               Event Espresso
 * @subpackage
 * @author                Mike Nelson
 */
class TableAnalysis extends EE_Base
{
    /**
     * The maximum number of characters that can be indexed on a column using utf8mb4 collation,
     * see https://events.codebasehq.com/redirect?https://make.wordpress.org/core/2015/04/02/the-utf8mb4-upgrade/
     */
    public const INDEX_COLUMN_SIZE  = 191;

    public const INDEX_TYPE_INDEX   = 'INDEX';

    public const INDEX_TYPE_UNIQUE  = 'UNIQUE';

    private array $known_tables = [];


    /**
     * Returns the table name which will definitely have the wpdb prefix on the front,
     * except if it currently has the wpdb->base_prefix on the front, in which case
     * it will have the wpdb->base_prefix on it
     *
     * @param string $table_name
     * @return string $tableName, having ensured it has the wpdb prefix on the front
     * @global wpdb  $wpdb
     */
    public function ensureTableNameHasPrefix(string $table_name): string
    {
        global $wpdb;
        return strpos($table_name, $wpdb->base_prefix) === 0 ? $table_name : $wpdb->prefix . $table_name;
    }


    /**
     * Indicates whether the table has any entries. $table_name can
     * optionally start with $wpdb->prefix or not
     *
     * @param string $table_name
     * @return bool
     * @global wpdb  $wpdb
     */
    public function tableIsEmpty(string $table_name): bool
    {
        global $wpdb;
        $table_name = $this->ensureTableNameHasPrefix($table_name);
        if ($this->tableExists($table_name)) {
            $count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
            return absint($count) === 0;
        }
        return false;
    }


    /**
     * Indicates whether the table exists. $table_name can optionally
     * have the $wpdb->prefix on the beginning, or not.
     *
     * @param string $table_name
     * @return bool
     * @global wpdb  $wpdb
     * @global array $EZSQL_ERROR
     */
    public function tableExists(string $table_name): bool
    {
        $table_name = $this->ensureTableNameHasPrefix($table_name);
        if (isset($this->known_tables[ $table_name ])) {
            return true;
        }
        global $wpdb, $EZSQL_ERROR;
        // ignore if this causes an SQL error
        $old_error             = $wpdb->last_error;
        $old_suppress_errors   = $wpdb->suppress_errors();
        $old_show_errors_value = $wpdb->show_errors(false);
        $ezsql_error_cache     = $EZSQL_ERROR;
        $wpdb->get_results("SELECT * from $table_name LIMIT 1");
        $wpdb->show_errors($old_show_errors_value);
        $wpdb->suppress_errors($old_suppress_errors);
        $new_error        = $wpdb->last_error;
        $wpdb->last_error = $old_error;
        $EZSQL_ERROR      = $ezsql_error_cache;
        // if there was a table doesn't exist error
        if (! empty($new_error)) {
            if (
                in_array(
                    EEH_Activation::last_wpdb_error_code(),
                    [
                        1051, // bad table
                        1109, // unknown table
                        117, // no such table
                    ]
                )
                || preg_match(
                    '~^Table .* doesn\'t exist~',
                    $new_error
                ) // in case not using mysql and error codes aren't reliable, just check for this error string
            ) {
                return false;
            }
            // log this because that's weird. Just use the normal PHP error log
            error_log(
                sprintf(
                    esc_html__(
                        'Event Espresso error detected when checking if table existed: %1$s (it wasn\'t just that the table didn\'t exist either)',
                        'event_espresso'
                    ),
                    $new_error
                )
            );
        }
        $this->known_tables[ $table_name ] = true;
        return true;
    }


    /**
     * @param string $table_name
     * @param string $index_name
     * @return array of columns used on that index, Each entry is an object with the following properties {
     * }
     */
    public function showIndexes(string $table_name, string $index_name): array
    {
        global $wpdb;
        if (! $this->tableExists($table_name)) {
            return [];
        }
        $table_name         = $this->ensureTableNameHasPrefix($table_name);
        $index_exists_query = "SHOW INDEX FROM $table_name WHERE Key_name = '$index_name'";
        return $wpdb->get_results($index_exists_query);
    }


    /**
     * @param string $table_name
     * @param string $index_name
     * @return bool
     * @since 5.0.13.p
     */
    public function indexExists(string $table_name, string $index_name): bool
    {
        global $wpdb;
        $table_name         = $this->ensureTableNameHasPrefix($table_name);
        $index_exists_query = "SHOW INDEX FROM $table_name WHERE key_name = '$index_name'";
        // using get_var with the $index_exists_query returns the table's name
        return $this->tableExists($table_name) && $wpdb->get_var($index_exists_query) === $table_name;
    }
}
