<?php

namespace CleantalkSP\SpbctWP\Scanner\ScannerActions;

use CleantalkSP\SpbctWP\Scanner\Helper;

class ScanResultsTableActions
{
    /**
     * @param $file_id
     *
     * @return string[]|true[]
     */
    public static function approveFile($file_id)
    {
        global $wpdb;

        $root_path = spbc_get_root_path();

        if ($file_id) {
            // Getting file info.
            $sql        = $wpdb->prepare('SELECT path, status, severity, pscan_status, pscan_processing_status, pscan_balls, pscan_file_id
			FROM ' . SPBC_TBL_SCAN_FILES . '
			WHERE fast_hash = %s
			LIMIT 1', $file_id);
            $sql_result = $wpdb->get_results($sql, ARRAY_A);
            $file_info  = $sql_result[0];

            if ( ! empty($file_info)) {
                if (file_exists($root_path . $file_info['path'])) {
                    if (is_readable($root_path . $file_info['path'])) {
                        // Getting file && API call
                        $previous = json_encode(array(
                                                    'status'   => $file_info['status'],
                                                    'severity' => $file_info['severity'],
                                                    'pscan_status' => $file_info['pscan_status'],
                                                    'pscan_processing_status' => $file_info['pscan_processing_status'],
                                                    'pscan_balls' => $file_info['pscan_balls'],
                                                    'pscan_file_id' => $file_info['pscan_file_id'],
                                                ));

                        // Updating all other statuses
                        $wpdb->update(
                            SPBC_TBL_SCAN_FILES,
                            array(
                                'status'         => 'APPROVED_BY_USER',
                                'previous_state' => $previous,
                                'pscan_pending_queue' => null,
                                'pscan_status' => null,
                                'pscan_processing_status' => null,
                                'pscan_balls' => null,
                                'pscan_file_id' => null,
                            ),
                            array('fast_hash' => $file_id),
                            array('%s', '%s', '%s'),
                            array('%s')
                        );

                        // Set severity to NULL
                        $sql = $wpdb->prepare(
                            'UPDATE ' . SPBC_TBL_SCAN_FILES . ' SET severity = NULL WHERE fast_hash = %s',
                            $file_id
                        );
                        $sql_result = $wpdb->query($sql);

                        if ($sql_result !== false) {
                            $output = array('success' => true);
                        } else {
                            $output = array('error' => 'DB_COULDNT_UPDATE_ROW_APPROVE');
                        }
                    } else {
                        $output = array('error' => 'FILE_NOT_READABLE');
                    }
                } else {
                    $output = array('error' => 'FILE_NOT_EXISTS');
                }
            } else {
                $output = array('error' => 'FILE_NOT_FOUND');
            }
        } else {
            $output = array('error' => 'WRONG_FILE_ID');
        }

        return $output;
    }

    /**
     * query
     * Remove file from the database
     * @param int|string $file_id
     * @return bool
     */
    public static function removeFileFromTable($file_id)
    {
        global $wpdb;

        return $wpdb->delete(SPBC_TBL_SCAN_FILES, array('fast_hash' => $file_id));
    }

    /**
     * tested
     * Get all files IDs of the category.
     *
     * @param string $category Category of files category which needs to be searched for
     * @param bool $just_count Whether to return the count of files or the files themselves
     *
     * @return array Array of IDs, or counter of files as first array key if $just_count is true.
     */
    public static function getFilesByCategory($category, $just_count = false)
    {
        global $wpdb;

        $ids = array();

        // No need to select IDs for Frontend Malware Scanner
        if ( $category === 'frontend_malware' ) {
            return $ids;
        }

        $query = $just_count ? 'SELECT COUNT(fast_hash) as counter from ' : 'SELECT fast_hash from ';
        $query .= SPBC_TBL_SCAN_FILES . Helper::getSQLWhereAddictionForTableOfCategory($category);

        // suppress because data is static
        // @psalm-suppress WpdbUnsafeMethodsIssue
        $res = $wpdb->get_results($query);

        if ($just_count) {
            return isset($res[0], $res[0]->counter) ? [$res[0]->counter] : [];
        }

        foreach ($res as $tmp) {
            $ids[] = isset($tmp->fast_hash) ? $tmp->fast_hash : null;
        }

        return $ids;
    }

    /**
     * tested
     * Changing file information after recovery
     * @param string $file_path
     * @param array $backup_prev_results_state
     * @return array
     */
    public static function updateScanResults($file_path, $backup_prev_results_state)
    {
        global $wpdb;

        if ($file_path && $backup_prev_results_state !== null) {
            $sql_prepared = $wpdb->prepare(
                'UPDATE ' . SPBC_TBL_SCAN_FILES . ' SET weak_spots = %s, checked_heuristic = %s, checked_signatures = %s, status = %s, severity = %s WHERE path = %s;',
                array(
                    $backup_prev_results_state['weak_spots'],
                    $backup_prev_results_state['checked_heuristic'],
                    $backup_prev_results_state['checked_signatures'],
                    $backup_prev_results_state['status'],
                    $backup_prev_results_state['severity'],
                    $file_path
                )
            );
            if ($wpdb->query($sql_prepared) === false) {
                return array('error' => esc_html__('Error update scan results: Something is wrong during saving previous state of file.', 'security-malware-firewall'));
            }
        } else {
            return array('error' => esc_html__('Error update scan results: Incorrect data for changing the recovery results about the file', 'security-malware-firewall'));
        }

        return array();
    }

    /**
     * query
     * Get file by ID
     * @param string $file_id
     * @return array|false
     */
    public static function getFileByID($file_id)
    {
        global $wpdb;

        $file_info = $wpdb->get_row(
            $wpdb->prepare(
                'SELECT * FROM ' . SPBC_TBL_SCAN_FILES . ' WHERE fast_hash = %s LIMIT 1',
                $file_id
            ),
            ARRAY_A
        );

        return $file_info ?: false;
    }

    /**
     * deleteFileFromAnalysisLog
     * Delete file from analysis log
     * @param array $file_ids
     * @return array
     */
    public static function deleteFileFromAnalysisLog($file_ids)
    {
        global $wpdb;

        $file_ids_clean = [];
        $file_ids_string = '';
        $output = array('error' => false);

        // Validate if the ID is hash (SQL-clear)
        if (is_array($file_ids)) {
            $file_ids_clean = array_map(function ($_id) {
                if ( \CleantalkSP\Common\Validate::isHash($_id) ) {
                    return $_id;
                }
            }, $file_ids);
        }

        // Prepare file IDs string
        foreach ( $file_ids_clean as $id ) {
            $file_ids_string .= '"' . $id . '",';
        }

        if ( $file_ids_clean ) {
            $query = "UPDATE " . SPBC_TBL_SCAN_FILES . " SET 
            last_sent = null,
            pscan_status = null,
            pscan_processing_status = null,
            pscan_pending_queue = null,
            pscan_balls = null,
            pscan_file_id = null 
            WHERE fast_hash IN (" . trim($file_ids_string, ',') . ")";
            // suppress because data is already prepared in Validate::isHash method
            // @psalm-suppress WpdbUnsafeMethodsIssue
            $updated_rows = $wpdb->query($query);

            if ( ! $updated_rows) {
                $output = array('error' => 'DB_ERROR');
            }
        } else {
            $output = array('error' => 'WRONG_FILE_ID');
        }

        return $output;
    }

    public static function disapproveFile($file_id = null)
    {
        global $wpdb;

        $time_start = microtime(true);
        $root_path = spbc_get_root_path();

        if (!$file_id) {
            return array('error' => 'WRONG_FILE_ID');
        }

        // Getting file info.
        $sql = $wpdb->prepare('SELECT path, full_hash, previous_state
        FROM ' . SPBC_TBL_SCAN_FILES . '
        WHERE fast_hash = %s
        LIMIT 1', $file_id);
        $sql_result = $wpdb->get_results($sql, ARRAY_A);
        $file_info  = isset($sql_result[0]) ? $sql_result[0] : null;

        if (empty($file_info)) {
            return array('error' => 'FILE_NOT_FOUND');
        }

        if (!file_exists($root_path . $file_info['path'])) {
            return array('error' => 'FILE_NOT_EXISTS');
        }

        if (!is_readable($root_path . $file_info['path'])) {
            return array('error' => 'FILE_NOT_READABLE');
        }

        $previous = is_string($file_info['previous_state'])
            ? json_decode($file_info['previous_state'], true)
            : false;

        // Placeholders for the approved by CT files
        if (!$previous) {
            $previous['status'] = 'OK';
            $previous['severity'] = null;
            $previous['pscan_status'] = null;
            $previous['pscan_processing_status'] = null;
            $previous['pscan_balls'] = null;
            $previous['pscan_file_id'] = null;
        }

        $sql_upd_result = $wpdb->update(
            SPBC_TBL_SCAN_FILES,
            array(
                'status' => $previous['status'],
                'severity' => $previous['severity'],
                'pscan_status' => $previous['pscan_status'],
                'pscan_processing_status' => $previous['pscan_processing_status'],
                'pscan_balls' => $previous['pscan_balls'],
                'pscan_file_id' => $previous['pscan_file_id'],
            ),
            array('fast_hash' => $file_id),
            array('%s', '%s', '%s', '%s', '%s', '%d'),
            array('%s')
        );

        if ($sql_upd_result === false) {
            return array('error' => 'DB_COULDNT_UPDATE_ROW_APPROVE');
        }

        return array(
            'success' => true,
            'exec_time' => round(microtime(true) - $time_start),
        );
    }

    /**
     * Disapprove files in bulk
     * @param array $ids
     * @return array
     */
    public static function bulkDisapproveFile($ids = array())
    {
        if ( ! $ids ) {
            return array('error' => 'Nothing to disapprove');
        }

        $out = array();

        foreach ($ids as $id) {
            $result = self::disapproveFile($id);

            if ( ! empty($result['error']) ) {
                $file_info             = self::getFileByID($id);
                $file_path             = isset($file_info['path']) ? $file_info['path'] : 'UNKNOWN_FILE';
                $out['error']          = __('Some files were not updated.', 'security-malware-firewall');
                $out['error_detail'][] = array(
                    'file_path' => $file_path,
                    'error'     => $result['error'],
                );
            }
        }

        return $out;
    }

    /**
     * Approve files in bulk
     * @param array $ids
     * @return array
     */
    public static function bulkApproveFile($ids = array())
    {
        if ( ! $ids) {
            return array('error' => 'Noting to approve');
        }

        $out = array();

        foreach ($ids as $id) {
            $result = self::approveFile($id);

            if ( ! empty($result['error'])) {
                $file_info             = self::getFileByID($id);
                $file_path             = isset($file_info['path']) ? $file_info['path'] : 'UNKNOWN_FILE';
                $out['error']          = __('Some files were not updated.', 'security-malware-firewall');
                $out['error_detail'][] = array(
                    'file_path' => $file_path,
                    'error'     => $result['error'],
                );
            }
        }

        return $out;
    }
}
