<?php

namespace RemoveUnusedMedia\API;

require_once "rum_util.php";
require_once "rum_db.php";
require_once "rum_queue.php";

use RemoveUnusedMedia\Core as Core;
use RemoveUnusedMedia\Util as Util;
use RemoveUnusedMedia\DB as DB;
use Throwable;

/**
 * Called from rum_html.php. Creates AJAX-js code
 */
function ajax_script()
{
    ?>
    <script type="text/javascript">
        let analyzing = <?= get_option('rum_analysis_running') ? 'true' : 'false' ?>;
        let resetRequested = <?= empty($_REQUEST['rum-reset']) ? 'false' : 'true'?>;

        function triggerAnalysis() {
            jQuery.post(ajaxurl, {'action': 'remove_unused_media', 'rum': 'trigger_analysis'});
            analysisIsRunning();
        }

        function analysisIsRunning() {
            document.getElementById('rum-overlay').style.display = 'block';
            analyzing = true;

            async function blink() {
                let loadingText = '<?php _e('Loading', 'remove-unused-media'); ?>';
                const dots = ["   ", ".  ", ".. ", "..."];
                let i = 0;
                while (analyzing) {
                    document.getElementById("rum-overlay-header").innerHTML = loadingText + dots[i];
                    await new Promise(r => setTimeout(r, 750));
                    i = (i + 1) % 4;
                }
            }

            blink();
            check_analysis();
        }

        async function check_analysis() {
            let done = false;
            while (!done) {
                await new Promise(r => setTimeout(r, 150));
                await jQuery.post(ajaxurl, {
                    'action': 'remove_unused_media',
                    'rum': 'check_analysis'
                }).done(function (res) {
                    if ('error' in res || 'done' in res) {
                        if ('error' in res) {
                            console.error(res['error']);
                        }
                        done = true;
                        location.reload();
                    } else {
                        if (res.status.media[0] > 0 && res.status.media[0] < res.status.media[1]) {
                            res.status.status += " " + Math.floor(res.status.media[0] * 100 / res.status.media[1]) + "%";
                        } else if (res.status.posts[0] > 0 && res.status.posts[0] < res.status.posts[1]) {
                            res.status.status += " " + Math.floor(res.status.posts[0] * 100 / res.status.posts[1]) + "%";
                        }
                        document.getElementById("analysis_status").innerText = res.status.status;
                    }
                });
            }
        }

        async function reset_analysis() {
            await new Promise(r => setTimeout(r, 5000));
            await jQuery.post(ajaxurl, {
                'action': 'remove_unused_media',
                'rum': 'reset_analysis'
            }).done(async function (res) {
                await new Promise(r => setTimeout(r, 5000));
                let url = new URL(window.location.href);
                url.searchParams.delete('rum-reset');
                window.location.href = url.toString();
            })
        }

        if (analyzing && !resetRequested) {
            analysisIsRunning();
        }
        if (resetRequested) {
            reset_analysis();
        }
    </script>
    <?php
    if (isset($_REQUEST['analytics']) || isset($_REQUEST['submit-error'])) {
        $url = remove_query_arg(['analytics', 'submit-error']); ?>
        <script type="text/javascript">
            window.history.pushState({}, document.title, "<?php echo $url; ?>");
        </script>
        <?php
    } ?>
    <script type="text/javascript">
        function showMoreWhere(id) {
            jQuery('#more-where-link-' + id).hide();
            jQuery('#more-where-placeholder-' + id).hide();
            jQuery('.more-where-' + id).show();
            jQuery('.more-how-' + id).show();
        }

        function toggleMoreHow(id, index) {
            let moreHowLink = jQuery('#more-how-link-' + id + '-' + index);
            if (moreHowLink.text() === ' \u25BC') {
                //collapsing
                moreHowLink.text(' \u25C0');
                jQuery('.how-' + id + '-' + index).hide();
            } else {
                //expanding
                moreHowLink.text(' \u25BC');
                jQuery('.how-' + id + '-' + index).show();
            }
        }
    </script> <?php
}

/**
 * add_action('load-' . $hook, 'RemoveUnusedMedia\rum_action');
 * Handles API calls
 */
function rum_action()
{
    if (isset($_REQUEST['rum_timeout'])) {
        if (is_numeric($_REQUEST['rum_timeout'])) {
            update_option('rum_timeout', $_REQUEST['rum_timeout']);
            Util\debug_logging("Timeout set to " . $_REQUEST['rum_timeout']);
            exit(wp_redirect(menu_page_url('rum', false)));
        }
    }
    if (isset($_REQUEST['analytics'])) {
        if ('opt-in' == $_REQUEST['analytics']) {
            Util\debug_logging("Analytics: Opt-in");
            update_option('rum_analytics', 'yes', false);
        } elseif ('opt-out' == $_REQUEST['analytics']) {
            Util\debug_logging("Analytics: Opt-out");
            update_option('rum_analytics', 'no', false);
        }
    }
    if (isset($_REQUEST['submit-error'])) {
        Util\debug_logging("Submit Error");
        $error        = get_option('rum_error');
        $error['url'] = get_site_url();
        $error['log'] = get_option('rum_log', []);
        wp_remote_post('https://remove-unused-media.com/analytics', ['body' => ['rum_error' => json_encode($error)]]);
        delete_option('rum_error');

        require_once 'rum_core.php';
        Core\analytics();
    }
    if (get_option('rum_analysis_running')) {
        Util\debug_logging("Analysis running, action aborted");

        return;
    }
    add_screen_option('per_page', ['option' => 'rum_per_page']);

    if (isset($_REQUEST['action'])) {
        Util\debug_logging("action " . $_REQUEST['action']);
        if ('delete' === $_REQUEST['action']) {
            action_delete($_REQUEST['media']);
        } elseif ('ignore' === $_REQUEST['action']) {
            $media = $_REQUEST['media'];
            action_ignore(is_numeric($media) ? [$media] : $media);
        } elseif ('unignore' === $_REQUEST['action']) {
            $media = $_REQUEST['media'];
            action_unignore(is_numeric($media) ? [$media] : $media);
        } elseif ('untrash' === $_REQUEST['action']) {
            action_untrash($_REQUEST['media']);
        } elseif ("-1" === $_REQUEST['action']) {
            if (isset($_REQUEST['delete_all'])) {
                $post_ids = DB\get_col('posts', 'ID', ['post_type' => "'attachment'", 'post_status' => "'trash'"]);
                foreach ($post_ids as $post_id_del) {
                    wp_delete_attachment($post_id_del);
                }
            }
        }
        if ('POST' !== $_SERVER['REQUEST_METHOD']) {
            Util\debug_logging("action (GET): return");
            $sendback = wp_get_referer();
            $sendback = remove_query_arg(
                [
                    'delete',
                    'ignore',
                    'unignore',
                    'media',
                    '_wpnonce',
                ],
                $sendback
            );
            wp_redirect($sendback);

            exit;
        }
    }
    if (isset($_REQUEST['trashed'])) {
        Util\debug_logging("trashed: " . json_encode($_REQUEST['ids']));
        DB\delete(DB\RESULT, ['id' => (int)$_REQUEST['ids']]);
    }
    if (isset($_REQUEST['untrashed'])) {
        Util\debug_logging("untrashed: " . json_encode($_REQUEST['ids']));
    }
}

function action_delete($media)
{
    if (count($media) == 0) {
        return;
    }
    Util\debug_logging("action delete: " . json_encode($_REQUEST['media']));
    foreach ($media as $m) {
        $m = (int)$m;
        wp_delete_attachment($m);
        DB\delete(DB\RESULT, ['id' => $m]);
    }
}

function action_untrash($media)
{
    Util\debug_logging("action untrash: " . json_encode($_REQUEST['media']));
    foreach ($media as $m) {
        wp_untrash_post((int)$m);
    }
}

function action_ignore($media)
{
    if (count($media) == 0) {
        return;
    }
    Util\debug_logging("action ignore: " . json_encode($media));
    $ignored = get_option('rum_ignored', []);
    foreach ($media as $m) {
        $ignored[] = (int)$m;
    }
    update_option('rum_ignored', $ignored);
}

function action_unignore($media)
{
    Util\debug_logging("action unignore: " . json_encode($media));
    $ignored = get_option('rum_ignored', []);
    foreach ($media as $m) {
        $m = (int)$m;
        $i = array_search($m, $ignored);
        if ($i !== false) {
            unset($ignored[$i]);
        }
    }
    update_option('rum_ignored', $ignored);
}

function trigger($data)
{
    $analysis_url = add_query_arg(
        ['action' => 'remove_unused_media', 'nonce' => wp_create_nonce('remove_unused_media')],
        admin_url('admin-ajax.php')
    );

    $filter  = function ($key) {
        return substr($key, 0, 9) === "wordpress";
    };
    $cookies = array_filter($_COOKIE, $filter, ARRAY_FILTER_USE_KEY);

    return wp_remote_post(
        $analysis_url, [
            'timeout'   => 0.01,
            'blocking'  => false,
            'cookies'   => $cookies,
            'sslverify' => apply_filters('https_local_ssl_verify', false),
            'body'      => $data,
        ]
    );
}

/**
 * Handles ajax requests
 */
function rum_ajax()
{
    global $rum_fs;

    if (!isset($_REQUEST['rum'])) {
        return;
    }
    //Util\debug_logging("ajax: {$_REQUEST['rum']}");
    switch ($_REQUEST['rum']) {
        case 'analyze':
            check_ajax_referer('remove_unused_media', 'nonce');
            analyze();
            wp_send_json_success();
            break;
        case 'trigger_analysis':
            if (get_option('rum_analysis_running')) {
                wp_send_json_error();
            }

            \RemoveUnusedMedia\Queue\start_analysing_queue();

            wp_send_json_success();
            break;
        case 'check_analysis':
            wp_cache_delete('rum_analysis_running', 'options');
            $status = get_option('rum_analysis_running', false);

            \RemoveUnusedMedia\Queue\cron_process_queue();

            if ($status) {
                wp_send_json(['status' => $status]);
            }
            wp_send_json(['done' => true]);
            break;
        case 'reset_analysis':
            delete_option('rum_analysis_running');
            delete_option('rum_error');
            delete_option('rum_crashed');
            Util\debug_logging("analysis reset");
            break;
    }
}

class shutdownScheduler
{
    private $active;
    private $buffer;

    public function __construct()
    {
        $this->active = true;
        $this->buffer = str_repeat('*', 1024 * 1024);
        register_shutdown_function([$this, 'shutdown']);
    }

    public function deactivate()
    {
        $this->active = false;
        $this->buffer = null;
    }

    public function shutdown()
    {
        if (!$this->active) {
            return;
        }
        $this->buffer = null;

        $status = get_option('rum_analysis_running');
        if ($status['media'][0] < $status['media'][1]) {
            $at = ['media', $status['media'][0], 0];
        } elseif ($status['posts'][0] < $status['posts'][1]) {
            $at = ['posts', $status['posts'][0], 0];
        } else {
            $at = ['cleanup', 0, 0];
        }
        $prev = get_option('rum_crashed');
        if ($prev && $prev[0] == $at[0] && $prev[1] == $at[1]) {
            if ($prev[2] == 0) {
                Util\debug_logging("2nd Crash at $at[0] #$at[1]");
                sleep(10);#wait for memory to clear
                $at[2] = 1;
            } else {
                Util\debug_logging("3rd crash at $at[0] #$at[1]; aborting");
                #TODO error report
                exit;
            }
        }
        update_option('rum_crashed', $at);
        Util\debug_logging("analysis crashed, restarting at $at[0] #$at[1]");
        trigger(['rum' => 'analyze']);
    }
}

/**
 * Triggers analysis
 */
function analyze()
{
    wp_cache_delete('rum_analysis_running', 'options');
    try {
        require_once 'rum_core.php';
        $shutdown = new shutdownScheduler();
        $result = Core\analyze();
        $shutdown->deactivate();
        if (!$result) {
            Util\debug_logging("analysis paused");
            trigger(['rum' => 'analyze']);
            exit;
        }
        Util\debug_logging("analysis complete");
        update_option('rum_time', current_time('timestamp'));
        $analysis_counter = get_option('rum_analysis_counter', 0);
        ++$analysis_counter;
        update_option('rum_analysis_counter', $analysis_counter, false);
        if (!get_option('rum_dismissed_backup', false) && $analysis_counter >= 3) {
            update_option('rum_dismissed_backup', true, false);
        }
        delete_option('rum_analysis_running');
        delete_option('rum_error');
        delete_option('rum_crashed');
    } catch (Throwable $e) {
        log_error($e);
    }
}

function log_error($e)
{
    $error_message = "Caught exception: {$e->getMessage()}\n";
    update_option('rum_error', [
        'error'        => $error_message,
        'error_detail' => [
            $e->getMessage(),
            $e->getCode(),
            $e->getFile(),
            $e->getLine(),
            $e->getTrace(),
            $e->getTraceAsString(),
        ],
    ], false);
    Util\reset();
}
