<?php

declare(strict_types=1);

namespace Duplicator\Ajax;

use Duplicator\Utils\Logging\DupLog;
use Duplicator\Models\GlobalEntity;
use Duplicator\Models\ActivityLog\LogEventSettingsChange;
use Duplicator\Core\CapMng;
use Duplicator\Core\Constants;
use Duplicator\Core\MigrationMng;
use Duplicator\Libs\Snap\SnapIO;
use Duplicator\Libs\Snap\SnapServer;
use Duplicator\Libs\Snap\SnapString;
use Duplicator\Libs\Snap\SnapUtil;
use Duplicator\Models\DynamicGlobalEntity;
use Duplicator\Models\SystemGlobalEntity;
use Duplicator\Utils\Logging\TraceLogMng;
use Duplicator\Utils\Settings\MigrateSettings;
use Duplicator\Utils\ZipArchiveExtended;
use Exception;

class ServicesSettings extends AbstractAjaxService
{
    /**
     * Init ajax calls
     *
     * @return void
     */
    public function init(): void
    {
        $this->addAjaxCall('wp_ajax_duplicator_get_trace_log', 'getTraceLog');
        $this->addAjaxCall('wp_ajax_duplicator_delete_trace_log', 'deleteTraceLog');
        $this->addAjaxCall('wp_ajax_duplicator_export_settings', 'exportSettings');
        $this->addAjaxCall('wp_ajax_duplicator_quick_fix', 'quickFix');
    }

    /**
     * Hook ajax wp_ajax_duplicator_get_trace_log
     *
     * @return void
     */
    public function getTraceLog(): void
    {
        AjaxWrapper::fileDownload(
            [
                self::class,
                'getTraceLogCallback',
            ],
            'duplicator_get_trace_log',
            SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'nonce'),
            CapMng::CAP_CREATE
        );
    }

    /**
     * Get trace log callback — creates a ZIP of trace files and returns file info for download.
     *
     * @return array{path: string, name: string}
     */
    public static function getTraceLogCallback(): array
    {
        DupLog::trace("enter");

        $zip_path = DUPLICATOR_SSDIR_PATH . "/" . Constants::ZIPPED_LOG_FILENAME;

        if (file_exists($zip_path)) {
            SnapIO::unlink($zip_path);
        }
        $zipArchive = new ZipArchiveExtended($zip_path);

        if ($zipArchive->open() == false) {
            throw new Exception('Can\'t open ZIP archive: ' . $zip_path);
        }

        foreach (TraceLogMng::getInstance()->getTraceFiles() as $traceFile) {
            if ($zipArchive->addFile($traceFile, basename($traceFile)) == false) {
                throw new Exception('Can\'t add ZIP file ' . basename($traceFile) . ' size: ' . filesize($traceFile));
            }
        }

        if ($zipArchive->close() === false) {
            throw new Exception('Failed to close ZIP archive: ' . $zip_path);
        }

        return [
            'path' => $zip_path,
            'name' => basename($zip_path),
        ];
    }

    /**
     * Hook ajax wp_ajax_duplicator_delete_trace_log
     *
     * @return void
     */
    public function deleteTraceLog(): void
    {
        AjaxWrapper::json(
            [
                self::class,
                'deleteTraceLogCallback',
            ],
            'duplicator_delete_trace_log',
            SnapUtil::sanitizeTextInput(INPUT_POST, 'nonce'),
            CapMng::CAP_CREATE
        );
    }

    /**
     * Delete trace log callback
     *
     * @return array<string, bool>
     */
    public static function deleteTraceLogCallback(): array
    {
        $res = DupLog::deleteTraceLog();
        if (!$res) {
            throw new Exception(__('Failed to delete trace log.', 'duplicator-pro'));
        }

        return ['success' => true];
    }

    /**
     * Hook ajax wp_ajax_duplicator_export_settings
     *
     * @return void
     */
    public function exportSettings(): void
    {
        AjaxWrapper::fileDownload(
            [
                self::class,
                'exportSettingsCallback',
            ],
            'duplicator_export_settings',
            SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'nonce'),
            CapMng::CAP_SETTINGS
        );
    }

    /**
     * Export settings callback — exports settings to a file and returns file info for download.
     *
     * @return array{path: string, name: string}
     */
    public static function exportSettingsCallback(): array
    {
        DupLog::trace("Export settings start");

        $message = '';
        if (($filePath = MigrateSettings::export($message)) === false) {
            throw new Exception($message);
        }

        // Log the settings export action
        LogEventSettingsChange::create(LogEventSettingsChange::SUB_TYPE_IMPORT_EXPORT, [
            'changes'     => [],
            'action_type' => 'settings_export',
        ]);

        return [
            'path' => $filePath,
            'name' => basename($filePath),
        ];
    }

    /**
     * Set default quick fix values automaticaly to help user
     *
     * @return void
     */
    public function quickFix(): void
    {
        AjaxWrapper::json(
            [
                self::class,
                'quickFixCallback',
            ],
            'duplicator_quick_fix',
            SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'nonce'),
            CapMng::CAP_CREATE
        );
    }

    /**
     * Set default quick fix values automaticaly to help user
     *
     * @return array<string, mixed>
     */
    public static function quickFixCallback(): array
    {
        $json      = [
            'success' => false,
            'message' => '',
        ];
        $inputData = filter_input_array(INPUT_POST, [
            'id'    => [
                'filter'  => FILTER_SANITIZE_SPECIAL_CHARS,
                'flags'   => FILTER_REQUIRE_SCALAR,
                'options' => ['default' => false],
            ],
            'setup' => [
                'filter'  => FILTER_SANITIZE_SPECIAL_CHARS,
                'flags'   => FILTER_REQUIRE_ARRAY,
                'options' => ['default' => false],
            ],
        ]);

        /** @var array<string,mixed>|false */
        $setup = $inputData['setup'];
        $id    = $inputData['id'];

        if (!$id || empty($setup)) {
            throw new Exception(__("Invalid request.", 'duplicator-pro'));
        }

        $data      = [];
        $isSpecial = isset($setup['special']) && is_array($setup['special']) && count($setup['special']) > 0;

        /* ****************
         *  general setup
         * **************** */
        if (isset($setup['global']) && is_array($setup['global'])) {
            $global = GlobalEntity::getinstance();

            foreach ($setup['global'] as $object => $value) {
                $value = SnapString::getTypedVal($value);
                if (isset($global->$object)) {
                    // get current setup
                    $current = $global->$object;
                    // if setup is not the same - fix this
                    if ($current !== $value) {
                        // set new value
                        $global->$object = $value;
                        // check value
                        $data[$object] = $global->$object;
                    }
                }
            }
            $global->save();
        }

        /* ****************
         *  SPECIAL SETUP
         * **************** */
        if ($isSpecial) {
            $special              = $setup['special'];
            $basicAuth            = isset($special['set_basic_auth']) && $special['set_basic_auth'] == 1;
            $removeInstallerFiles = isset($special['remove_installer_files']) && $special['remove_installer_files'] == 1;

            /**
             * SPECIAL FIX: Set basic auth username & password
             * */
            if ($basicAuth) {
                $data = array_merge($data, self::quickFixBasicAuth());
            }

            /**
             * SPECIAL FIX: Remove installer files
             * */
            if ($removeInstallerFiles) {
                $data = array_merge($data, self::quickFixRemoveInstallerFiles());
            }
        }

        // Save new property
        $find = count($data);
        if ($find > 0) {
            $system_global = SystemGlobalEntity::getInstance();
            if (strlen($id) > 0) {
                $system_global->removeFixById($id);
                $json['id'] = $id;
            }

            $json['success']           = true;
            $json['setup']             = $data;
            $json['fixed']             = $find;
            $json['recommended_fixes'] = count($system_global->recommended_fixes);

            // Log settings change for quick fix
            LogEventSettingsChange::create(LogEventSettingsChange::SUB_TYPE_QUICK_FIX, [
                'changes'     => [], // Quick fixes don't have specific setting changes to track
                'action_type' => 'quick_fix_apply',
            ]);
        }

        return $json;
    }

    /**
     * Quick fix for removing installer files
     *
     * @return array{removed_installer_files:bool} $data
     */
    private static function quickFixRemoveInstallerFiles(): array
    {
        $data        = [];
        $fileRemoved = MigrationMng::cleanMigrationFiles();
        $removeError = false;
        if (count($fileRemoved) > 0) {
            $data['removed_installer_files'] = true;
        } else {
            throw new Exception(esc_html__("Unable to remove installer files.", 'duplicator-pro'));
        }
        return $data;
    }

    /**
     * Quick fix for basic auth
     *
     * @return array{basic_auth_enabled:bool,basic_auth_user:string,basic_auth_password:string}
     */
    private static function quickFixBasicAuth(): array
    {
        $dGlobal  = DynamicGlobalEntity::getInstance();
        $detected = SnapServer::detectBasicAuthCredentials();
        if ($detected === null || $detected['user'] === '' || $detected['password'] === '') {
            throw new Exception(__("Username or password were not set.", 'duplicator-pro'));
        }

        $username = $detected['user'];
        $password = $detected['password'];

        $data = [];
        $dGlobal->setValBool('basic_auth_enabled', true);
        $data['basic_auth_enabled'] = true;

        $dGlobal->setValString('basic_auth_user', $username);
        $data['basic_auth_user'] = $username;

        $dGlobal->setValString('basic_auth_password', $password);
        $data['basic_auth_password'] = "**Secure Info**";

        $dGlobal->save();

        return $data;
    }
}
