<?php

namespace WPStaging\Pro\Backup\Ajax\Export;

use wpdb;
use WPStaging\Core\WPStaging;
use WPStaging\Framework\Adapter\Directory;
use WPStaging\Framework\Analytics\Actions\AnalyticsBackupCreate;
use WPStaging\Framework\Facades\Sanitize;
use WPStaging\Framework\Filesystem\Filesystem;
use WPStaging\Framework\Security\Auth;
use WPStaging\Framework\Utils\Urls;
use WPStaging\Pro\Backup\Ajax\PrepareJob;
use WPStaging\Pro\Backup\BackupProcessLock;
use WPStaging\Pro\Backup\Dto\Job\JobExportDataDto;
use WPStaging\Pro\Backup\Exceptions\ProcessLockedException;
use WPStaging\Pro\Backup\Job\Jobs\JobExport;

class PrepareExport extends PrepareJob
{
    /** @var JobExportDataDto */
    private $jobDataDto;
    private $jobExport;
    private $urls;
    private $analyticsBackupCreate;

    /** @var wpdb */
    private $wpdb;

    public function __construct(Filesystem $filesystem, Directory $directory, Auth $auth, BackupProcessLock $processLock, Urls $urls, AnalyticsBackupCreate $analyticsBackupCreate)
    {
        parent::__construct($filesystem, $directory, $auth, $processLock);

        global $wpdb;

        $this->wpdb = $wpdb;
        $this->urls = $urls;
        $this->analyticsBackupCreate = $analyticsBackupCreate;
    }

    public function ajaxPrepare($data)
    {
        if (!$this->auth->isAuthenticatedRequest()) {
            wp_send_json_error(null, 401);
        }

        try {
            $this->processLock->checkProcessLocked();
        } catch (ProcessLockedException $e) {
            wp_send_json_error($e->getMessage(), $e->getCode());
        }

        $response = $this->prepare($data);

        if ($response instanceof \WP_Error) {
            wp_send_json_error($response->get_error_message(), $response->get_error_code());
        } else {
            $this->analyticsBackupCreate->enqueueStartEvent($this->jobDataDto->getId(), $this->jobDataDto);
            wp_send_json_success();
        }
    }

    public function prepare($data = null)
    {
        if (empty($data) && array_key_exists('wpstgExportData', $_POST)) {
            $data = Sanitize::sanitizeArray($_POST['wpstgExportData'], [
                'isExportingPlugins' => 'bool',
                'isExportingMuPlugins' => 'bool',
                'isExportingThemes' => 'bool',
                'isExportingUploads' => 'bool',
                'isExportingOtherWpContentFiles' => 'bool',
                'isExportingDatabase' => 'bool',
                'isAutomatedBackup' => 'bool',
                'repeatBackupOnSchedule' => 'bool',
                'scheduleRotation' => 'int',
                'isCreateScheduleBackupNow' => 'bool',
            ]);
        }

        try {
            $sanitizedData = $this->setupInitialData($data);
        } catch (\Exception $e) {
            return new \WP_Error(400, $e->getMessage());
        }

        return $sanitizedData;
    }

    private function setupInitialData($sanitizedData)
    {
        $sanitizedData = $this->validateAndSanitizeData($sanitizedData);
        $this->clearCacheFolder();

        // Lazy-instantiation to avoid process-lock checks conflicting with running processes.
        $services = WPStaging::getInstance()->getContainer();
        $this->jobDataDto = $services->make(JobExportDataDto::class);
        $this->jobExport = $services->make(JobExport::class);

        $this->jobDataDto->hydrate($sanitizedData);
        $this->jobDataDto->setInit(true);
        $this->jobDataDto->setFinished(false);
        $this->jobDataDto->setStartTime(time());

        $this->jobDataDto->setId(substr(md5(mt_rand() . time()), 0, 12));

        $this->jobExport->setJobDataDto($this->jobDataDto);

        return $sanitizedData;
    }

    /**
     * @return array
     */
    public function validateAndSanitizeData($data)
    {
        // Unset any empty value so that we replace them with the defaults.
        foreach ($data as $key => $value) {
            if (empty($value)) {
                unset($data[$key]);
            }
        }

        $sites = [];

        if (is_multisite()) {
            // @todo remove once we give user options to select sites from ui
            $sites = $this->wpdb->get_results("SELECT blog_id, site_id, domain, path FROM {$this->wpdb->base_prefix}blogs");
        }

        foreach ($sites as $site) {
            switch_to_blog($site->blog_id);
            $site->site_url = site_url();
            $site->home_url = home_url();
            restore_current_blog();
        }

        $defaults = [
            'name' => $this->urls->getBaseUrlWithoutScheme(),
            'isExportingPlugins' => false,
            'isExportingMuPlugins' => false,
            'isExportingThemes' => false,
            'isExportingUploads' => false,
            'isExportingOtherWpContentFiles' => false,
            'isExportingDatabase' => false,
            'isAutomatedBackup' => false,
            'repeatBackupOnSchedule' => false,
            'scheduleRecurrence' => '',
            'scheduleTime' => [0, 0],
            'scheduleRotation' => 1,
            // scheduleId will only be set for backups created automatically on a schedule.
            'scheduleId' => null,
            'storages' => [],
            'isCreateScheduleBackupNow' => false,
            'sitesToExport' => $sites
        ];

        $data = wp_parse_args($data, $defaults);

        // Make sure data has no keys other than the expected ones.
        $data = array_intersect_key($data, $defaults);

        // Make sure data has all expected keys.
        foreach ($defaults as $expectedKey => $value) {
            if (!array_key_exists($expectedKey, $data)) {
                throw new \UnexpectedValueException("Invalid request. Missing '$expectedKey'.");
            }
        }

        // Sanitize data
        $data['name'] = substr(sanitize_text_field(html_entity_decode($data['name'])), 0, 100);

        // Foo\'s Backup => Foo's Backup
        $data['name'] = str_replace('\\\'', '\'', $data['name']);

        // Remove accents and disallow most special characters?
        // $data['name'] = remove_accents($data['name']);
        // $data['name'] = preg_replace('#[^a-zA-Z0-9\' "]#', '', $data['name']);

        $data['isExportingPlugins'] = $this->jsBoolean($data['isExportingPlugins']);
        $data['isExportingMuPlugins'] = $this->jsBoolean($data['isExportingMuPlugins']);
        $data['isExportingThemes'] = $this->jsBoolean($data['isExportingThemes']);
        $data['isExportingUploads'] = $this->jsBoolean($data['isExportingUploads']);
        $data['isExportingOtherWpContentFiles'] = $this->jsBoolean($data['isExportingOtherWpContentFiles']);
        $data['isExportingDatabase'] = $this->jsBoolean($data['isExportingDatabase']);

        $data['repeatBackupOnSchedule'] = $this->jsBoolean($data['repeatBackupOnSchedule']);
        $data['scheduleRecurrence'] = sanitize_text_field(html_entity_decode($data['scheduleRecurrence']));
        $data['scheduleRotation'] = absint($data['scheduleRotation']);

        // Depending on whether the data is coming from JavaScript or being hydrated, this can be an array [0,0] or a string 00:00
        if (is_array($data['scheduleTime'])) {
            $data['scheduleTime'] = implode(':', $data['scheduleTime']);
        }

        if (preg_match('#\d+:\d+#', $data['scheduleTime'])) {
            $data['scheduleTime'] = explode(':', $data['scheduleTime']);
        } else {
            $data['scheduleTime'] = [0, 0];
        }

        $data['isCreateScheduleBackupNow'] = $this->jsBoolean($data['isCreateScheduleBackupNow']);

        return $data;
    }

    /**
     * Returns the reference to the current Export Job, if any.
     *
     * @return JobExport|null The current reference to the Export Job, if any.
     */
    public function getJobExport()
    {
        return $this->jobExport;
    }

    /**
     * Persists the current Job Export status.
     *
     * @return bool Whether the current Job status was persisted or not.
     */
    public function persist()
    {
        if (!$this->jobExport instanceof JobExport) {
            return false;
        }

        $this->jobExport->persist();

        return true;
    }
}
