<?php

use PhpOffice\PhpSpreadsheet\Shared\Date;

defined('ABSPATH') or die('Access denied.');

class WPDataTableCache
{

    public static function maybeCache($createCache, $tableID)
    {
        if ($tableID) {

            if (!$createCache) {
                self::delete($tableID);
                return false;
            }

            $cache = self::isCacheDataExist($tableID);

            if (!$cache) {
                return false;
            }

            if (isset($_POST['action']) && $_POST['action'] === 'wpdatatables_save_table_config') {
                self::delete($tableID);
                return false;
            }

            if (!isset($cache->data))
                return false;

            return json_decode($cache->data, true);
        }

        return false;
    }

    private static function updateData($tableID, $sourceData)
    {
        global $wpdb;
        if ($tableID) {
            $sourceData = self::filterUserData($sourceData, $tableID);
            $wpdb->update(
                $wpdb->prefix . "wpdatatables_cache",
                array(
                    'data' => json_encode($sourceData, JSON_NUMERIC_CHECK),
                    'updated_time' => current_time('mysql'),
                ),
                array('table_id' => $tableID)
            );
            if ($wpdb->last_error !== '') {
                self::_logErrors('Update cache error:', $wpdb->last_error, 1, $tableID);
            }
        } else {
            self::_logErrors('Update cache error:', 'Table ID is not set for auto update data.', 1, $tableID);
        }
    }

    private static function filterUserData($sourceData, $tableID)
    {
        $filterSourceData = apply_filters('wpdatatables_filter_source_data_on_auto_update_cache', true, $tableID);
        if (!current_user_can('unfiltered_html') && $filterSourceData) {
            $tempSourceData = $sourceData;
            $sourceData = [];
            foreach ($tempSourceData as $index => $tempData) {
                foreach ($tempData as $key => $data) {
                    $sourceData[$index][wp_kses_post($key)] = is_null($data) ? sanitize_text_field($data) : wp_kses_post($data);
                }
            }
            return $sourceData;
        }
        return $sourceData;
    }

    public static function maybeSaveData($tableID, $tableType, $tableContent, $autoUpdate, $sourceData, $isCache)
    {
        global $wpdb;
        if ($tableID && $isCache) {
            if (!empty($sourceData)) {
                $sourceData = self::filterUserData($sourceData, $tableID);
                $wpdb->insert(
                    $wpdb->prefix . "wpdatatables_cache",
                    array(
                        'table_id' => $tableID,
                        'table_type' => $tableType,
                        'table_content' => $tableContent,
                        'auto_update' => $autoUpdate,
                        'data' => json_encode($sourceData, JSON_NUMERIC_CHECK)
                    )
                );
                if ($wpdb->last_error !== '')
                    self::_logErrors('Save cache error:', $wpdb->last_error, 0, $tableID);

            }
        }
    }

    private static function delete($tableID)
    {
        if ($tableID) {
            global $wpdb;
            $wpdb->delete(
                $wpdb->prefix . "wpdatatables_cache",
                array(
                    'table_id' => $tableID
                ),
                array(
                    '%d'
                )
            );
            if ($wpdb->last_error !== '')
                self::_logErrors('Delete cache error:', $wpdb->last_error, 0, $tableID);
        }
    }

    private static function isCacheDataExist($tableID)
    {
        global $wpdb;
        $cacheQuery = $wpdb->prepare(
            "SELECT data
                        FROM " . $wpdb->prefix . "wpdatatables_cache 
                        WHERE table_id = %d",
            $tableID
        );

        $cache = $wpdb->get_row($cacheQuery);

        if ($wpdb->last_error !== '') {
            self::_logErrors('Get cache data error:', $wpdb->last_error, 0, $tableID);
            return false;
        }

        if ($cache === null) {
            return false;
        }

        return $cache;
    }

    private static function getTablesWithCacheForAutoUpdate()
    {
        global $wpdb;
        $tablesForAutoUpdateQuery = "SELECT table_id, table_type, table_content, updated_time, data
                                              FROM " . $wpdb->prefix . "wpdatatables_cache 
                                              WHERE auto_update = 1
                                              ORDER BY id";

        $tablesForAutoUpdate = $wpdb->get_results($tablesForAutoUpdateQuery, ARRAY_A);

        if ($wpdb->last_error !== '') {
            self::_logErrors('Error get tables with cache:', $wpdb->last_error, 0, 0);
            return false;
        }

        if ($tablesForAutoUpdate === null) {
            return false;
        }

        return $tablesForAutoUpdate;
    }

    public static function addAutoUpdateHooks()
    {
        add_action('wp_ajax_wdtable_update_cache', array(__CLASS__, 'maybeAutoUpdate'));
        add_action('wp_ajax_nopriv_wdtable_update_cache', array(__CLASS__, 'maybeAutoUpdate'));
    }

    private static function _logErrors($title, $log, $autoUpdate, $tableID)
    {
        global $wpdb;
        $logMessage = 'wpDataTables - ';

        if ($title) {
            $logMessage = $logMessage . $title;
        }
        $logMessage = $logMessage . ' ' . $log;

        if ($tableID) {
            $logMessage = $logMessage . ' Table ID=' . $tableID;
            if ($autoUpdate) {
                $logError = current_time('mysql') . ' - ' . $title . ' ' . $log;
                $wpdb->query(
                    $wpdb->prepare(
                        "UPDATE " . $wpdb->prefix . "wpdatatables_cache
                           SET log_errors = %s WHERE table_id = %d",
                        $logError,
                        $tableID
                    )
                );
            }
        }

        error_log($logMessage);
    }

    public static function maybeAutoUpdate()
    {
        $autoUpdateHash = get_option('wdtAutoUpdateHash');

        if ($autoUpdateHash !== $_GET['wdtable_cache_verify']) return;

        $cacheTables = self::getTablesWithCacheForAutoUpdate();

        if (!$cacheTables) {
            return;
        }

        foreach ($cacheTables as $cacheTable) {

            $result = self::_renderDataFromSource(
                $cacheTable['table_id'],
                $cacheTable['table_type'],
                $cacheTable['table_content']
            );

            if (isset($result['status']) && $result['status'] === 'success') {
                if (!isset($result['data'])) {
                    self::_logErrors('Auto update error message:', 'Data array from source is not rendered.', 1, $cacheTable['table_id']);
                    continue;
                }

                $cacheData = json_decode($cacheTable['data'], true);

                if (!isset($cacheData[0])) {
                    self::_logErrors('Auto update error message:', 'Data array from cache is empty.', 1, $cacheTable['table_id']);
                    continue;
                }

                if (!isset($result['data'][0])) {
                    self::_logErrors('Auto update error message:', 'Data array from source is not rendered.', 1, $cacheTable['table_id']);
                    continue;
                }

                if (count($cacheData[0]) !== count($result['data'][0])) {
                    self::_logErrors('Auto update error message:', 'Data array from source and cache do not have same number of keys(columns).', 1, $cacheTable['table_id']);
                    continue;
                }

                if (array_keys($cacheData[0]) !== array_keys($result['data'][0])) {
                    self::_logErrors('Auto update error message:', 'Data array from source and cache do not have same keys(columns).', 1, $cacheTable['table_id']);
                    continue;
                }

                if ($cacheTable['data'] === json_encode($result['data'], JSON_NUMERIC_CHECK)) continue;

                self::updateData($cacheTable['table_id'], $result['data']);
            }
        }
    }

    private static function _renderDataFromSource($table_id, $source_type, $source)
    {
        if (empty($source)) {
            return [
                'status' => 'error',
                'error' => 'Source is empty.',
                'data' => []
            ];
        }
        try {
            if (in_array($source_type, ['xlsx', 'ods', 'xls', 'csv'])) {
                $tableData = WDTConfigController::loadTableFromDB($table_id);
                $params = array(
                    'dateInputFormat' => array(),
                    'data_types' => array(),
                );
                if ($tableData) {
                    foreach ($tableData->columns as $column) {
                        if ($column->type !== 'autodetect') {
                            $params['data_types'][$column->orig_header] = $column->type;
                        }
                        $params['dateInputFormat'][$column->orig_header] = isset($column->dateInputFormat) ? $column->dateInputFormat : null;
                    }
                    $source = $tableData->content;
                }

            }
            $dataArray = array();
            switch ($source_type) {
                case 'ods':
                case 'xlsx':
                case 'xls':
                case 'csv':
                    ini_set('memory_limit', '2048M');
                    if (isset($tableData) && $tableData->file_location == 'wp_media_lib' && !file_exists($source)) {
                        self::_logErrors('Error message:', 'Provided file ' . stripcslashes($source) . ' does not exist!', 1, $table_id);
                    }
                    $format = substr(strrchr($source, "."), 1);
                    $objReader = WPDataTable::createObjectReader($source);
                    $tempFileName = 'tempfile.txt';
                    if (isset($tableData) && $tableData->file_location == 'wp_any_url') {
                        $data = WDTTools::curlGetData($source);
                        if ($data == null)
                            throw new WDTException(esc_html__("File from provided URL is empty."));
                        $tempFileName = 'tempfile' . $tableData->id . '.' . $format;
                        $fillFileWithData = file_put_contents($tempFileName, $data);
                        if ($fillFileWithData === false)
                            throw new WDTException(esc_html__("File from provided URL is empty."));
                        $source = $tempFileName;
                    }
                    $objPHPExcel = $objReader->load($source);
                    if (isset($tableData) && $tableData->file_location == 'wp_any_url') unlink($tempFileName);
                    $objWorksheet = $objPHPExcel->getActiveSheet();
                    $highestRow = $objWorksheet->getHighestRow();
                    $highestColumn = $objWorksheet->getHighestDataColumn();

                    $headingsArray = $objWorksheet->rangeToArray('A1:' . $highestColumn . '1', null, true, true, true);
                    $headingsArray = array_map('trim', $headingsArray[1]);

                    $r = -1;

                    $dataRows = $objWorksheet->rangeToArray('A2:' . $highestColumn . $highestRow, null, true, true, true);
                    for ($row = 2; $row <= $highestRow; ++$row) {
                        if (max($dataRows[$row]) !== null) {
                            ++$r;
                            foreach ($headingsArray as $dataColumnIndex => $dataColumnHeading) {
                                $dataColumnHeading = trim(preg_replace('/\s\s+/', ' ', str_replace("\n", " ", $dataColumnHeading)));
                                $dataArray[$r][$dataColumnHeading] = $dataRows[$row][$dataColumnIndex];
                                $currentDateFormat = isset($params['dateInputFormat'][$dataColumnHeading]) ? $params['dateInputFormat'][$dataColumnHeading] : null;
                                if (!empty($params['data_types'][$dataColumnHeading]) && in_array($params['data_types'][$dataColumnHeading], array('date',
                                        'datetime',
                                        'time'))) {
                                    if ($format === 'xls' || $format === 'ods') {
                                        $cell = $objPHPExcel->getActiveSheet()->getCell($dataColumnIndex . '' . $row);
                                        if (Date::isDateTime($cell) && $cell->getValue() !== null) {
                                            $dataArray[$r][$dataColumnHeading] = Date::excelToTimestamp($cell->getValue());
                                        } else {
                                            $dataArray[$r][$dataColumnHeading] = WDTTools::wdtConvertStringToUnixTimestamp($dataRows[$row][$dataColumnIndex], $currentDateFormat);
                                        }
                                    } elseif ($format === 'csv') {
                                        $dataArray[$r][$dataColumnHeading] = WDTTools::wdtConvertStringToUnixTimestamp($dataRows[$row][$dataColumnIndex], $currentDateFormat);
                                    }
                                }
                            }
                        }
                    }
                    break;
                case 'xml':
                    $dataArray = WPDataTable::xmlRenderData($source, $table_id);
                    break;
                case 'json':
                    $dataArray = WPDataTable::jsonRenderData($source, $table_id);
                    break;
                case 'nested_json':
                    $dataArray = WPDataTable::nestedJsonRenderData($source, $table_id);
                    break;
                case 'serialized':
                    $dataArray = WPDataTable::serializedPhpRenderData($source, $table_id);
                    break;
                case 'google_spreadsheet':
                    $dataArray = WPDataTable::googleRenderData($source);
                    break;
                default:
                    self::_logErrors('Error message:', 'Source type is unknown', 1, $table_id);
                    return [
                        'status' => 'error',
                        'error' => 'Source type is unknown',
                        'data' => []
                    ];
            }
        } catch (Exception $e) {
            self::_logErrors('Error message:', $e->getMessage(), 1, $table_id);
            return [
                'status' => 'error',
                'error' => $e->getMessage(),
                'data' => []
            ];
        }

        if (empty($dataArray)) {
            self::_logErrors('Error message:', 'Data from source is empty', 1, $table_id);
            return [
                'status' => 'error',
                'error' => 'Data from source is empty',
                'data' => []
            ];
        }

        return [
            'status' => 'success',
            'error' => '',
            'data' => $dataArray
        ];
    }
}

WPDataTableCache::addAutoUpdateHooks();