<?php

/**
 * @package     Dotclear
 *
 * @copyright   Olivier Meunier & Association Dotclear
 * @copyright   AGPL-3.0
 */
declare(strict_types=1);

namespace Dotclear\Plugin\antispam;

use Dotclear\App;
use Dotclear\Helper\Process\TraitProcess;
use Dotclear\Helper\Date;
use Dotclear\Helper\Html\Form\Button;
use Dotclear\Helper\Html\Form\Checkbox;
use Dotclear\Helper\Html\Form\Div;
use Dotclear\Helper\Html\Form\Fieldset;
use Dotclear\Helper\Html\Form\Form;
use Dotclear\Helper\Html\Form\Hidden;
use Dotclear\Helper\Html\Form\Img;
use Dotclear\Helper\Html\Form\Legend;
use Dotclear\Helper\Html\Form\Li;
use Dotclear\Helper\Html\Form\Link;
use Dotclear\Helper\Html\Form\Number;
use Dotclear\Helper\Html\Form\Para;
use Dotclear\Helper\Html\Form\Strong;
use Dotclear\Helper\Html\Form\Submit;
use Dotclear\Helper\Html\Form\Table;
use Dotclear\Helper\Html\Form\Tbody;
use Dotclear\Helper\Html\Form\Td;
use Dotclear\Helper\Html\Form\Text;
use Dotclear\Helper\Html\Form\Th;
use Dotclear\Helper\Html\Form\Thead;
use Dotclear\Helper\Html\Form\Tr;
use Dotclear\Helper\Html\Form\Ul;
use Dotclear\Helper\Html\Html;
use Exception;

/**
 * @brief   The module manage process.
 * @ingroup antispam
 */
class Manage
{
    use TraitProcess;

    /**
     * @var array<string, SpamFilter> $filters
     */
    protected static array $filters;

    protected static SpamFilter $filter;    // Mirrored in App::backend()->filter

    protected static false|string $filter_gui;

    public static function init(): bool
    {
        return self::status(My::checkContext(My::MANAGE));
    }

    public static function process(): bool
    {
        if (!self::status()) {
            return false;
        }

        Antispam::initFilters();

        self::$filters    = Antispam::$filters->getFilters();
        self::$filter_gui = false;

        App::backend()->default_tab = null;

        // May be used by 3rd party code
        App::backend()->filter = null;

        try {
            // Show filter configuration GUI
            $filter = isset($_GET['f']) && is_string($_GET['f']) ? $_GET['f'] : '';
            if ($filter !== '') {
                if (!isset(self::$filters[$filter])) {
                    throw new Exception(__('Filter does not exist.'));
                }

                if (!self::$filters[$filter]->hasGUI()) {
                    throw new Exception(__('Filter has no user interface.'));
                }

                self::$filter = self::$filters[$filter];

                // May be used by 3rd party code
                App::backend()->filter = self::$filter;

                self::$filter_gui = self::$filter->gui(self::$filter->guiURL() ?: '');
            }

            // Remove all spam
            if (!empty($_POST['delete_all'])) {
                $ts       = isset($_POST['ts']) && is_numeric($_POST['ts']) ? (int) $_POST['ts'] : null;
                $timezone = App::blog()->settings()->get('system')->getStr('blog_timezone');
                $datetime = Date::str('%Y-%m-%d %H:%M:%S', $ts, $timezone);

                Antispam::delAllSpam($datetime);

                App::backend()->notices()->addSuccessNotice(__('Spam comments have been successfully deleted.'));
                My::redirect();
            }

            // Update filters
            if (isset($_POST['filters_upd'])) {
                /**
                 * @var        array<string, array{bool, int, bool}>
                 */
                $filters_opt = [];
                $i           = 0;
                foreach (array_keys(self::$filters) as $filter_id) {
                    $filters_opt[$filter_id] = [false, $i++, false];
                }

                // Enable active filters
                if (isset($_POST['filters_active']) && is_array($_POST['filters_active'])) {
                    foreach ($_POST['filters_active'] as $filter_id) {
                        $filter_id = is_string($filter_id) ? $filter_id : '';
                        if ($filter_id !== '' && array_key_exists($filter_id, $filters_opt)) {
                            $filters_opt[$filter_id][0] = true;
                        }
                    }
                }

                // Order filters
                if (!empty($_POST['f_order']) && empty($_POST['filters_order'])) {
                    /**
                     * @var array<array-key, mixed>
                     */
                    $order = $_POST['f_order'];
                    asort($order);
                    /**
                     * @var list<string>
                     */
                    $order = array_keys($order);
                } elseif (!empty($_POST['filters_order'])) {
                    $filters_order = is_string($filters_order = $_POST['filters_order']) ? $filters_order : '';
                    /**
                     * @var non-empty-list<string>
                     */
                    $order = explode(',', trim($filters_order, ','));
                }

                if (isset($order)) {
                    foreach ($order as $i => $filter_id) {
                        if ($filter_id !== '' && array_key_exists($filter_id, $filters_opt)) {
                            $filters_opt[$filter_id][1] = $i;
                        }
                    }
                }

                // Set auto delete flag
                if (isset($_POST['filters_auto_del']) && is_array($_POST['filters_auto_del'])) {
                    foreach ($_POST['filters_auto_del'] as $filter_id) {
                        $filter_id = is_string($filter_id) ? $filter_id : '';
                        if ($filter_id !== '' && array_key_exists($filter_id, $filters_opt)) {
                            $filters_opt[$filter_id][2] = true;
                        }
                    }
                }

                Antispam::$filters->saveFilterOpts($filters_opt);

                App::backend()->notices()->addSuccessNotice(__('Filters configuration has been successfully saved.'));
                My::redirect();
            }
        } catch (Exception $exception) {
            App::error()->add($exception->getMessage());
        }

        return true;
    }

    public static function render(): void
    {
        if (!self::status()) {
            return;
        }

        $head = App::backend()->page()->jsPageTabs(App::backend()->default_tab);
        if (!App::auth()->prefs()->get('accessibility')->getBool('nodragdrop')) {
            $head .= App::backend()->page()->jsLoad('js/jquery/jquery-ui.custom.js') .
                App::backend()->page()->jsLoad('js/jquery/jquery.ui.touch-punch.js') .
                My::jsLoad('dragndrop');
        }

        $head .= App::backend()->page()->jsJson('antispam', ['confirm_spam_delete' => __('Are you sure you want to delete all spams?')]) .
            My::jsLoad('antispam') .
            My::cssLoad('style');

        if (self::$filter_gui !== false) {
            // Display filter GUI

            $title = sprintf(__('%s configuration'), self::$filter->name) . ' - ' . My::name();
            App::backend()->page()->openModule($title, $head);

            echo
            App::backend()->page()->breadcrumb(
                [
                    __('Plugins')                                               => '',
                    My::name()                                                  => App::backend()->getPageURL(),
                    sprintf(__('%s filter configuration'), self::$filter->name) => '',
                ]
            ) .
            App::backend()->notices()->getNotices();

            echo (new Para())
                ->items([
                    (new Link('back'))->href(App::backend()->getPageURL())->class('back')->text(__('Back to filters list')),
                ])
            ->render();

            echo self::$filter_gui;

            if (self::$filter->help) {
                App::backend()->page()->helpBlock(self::$filter->help);
            }
        } else {
            // Display list of filters

            App::backend()->page()->openModule(My::name(), $head);

            echo
            App::backend()->page()->breadcrumb(
                [
                    __('Plugins') => '',
                    My::name()    => '',
                ]
            ) .
            App::backend()->notices()->getNotices();

            // Information
            $spam_count      = Antispam::countSpam();
            $published_count = Antispam::countPublishedComments();
            $moderationTTL   = My::settings()->getInt('antispam_moderation_ttl', false);

            $action = [];
            if ($spam_count > 0) {
                $action = [
                    (new Para())->items([
                        (new Submit(['delete_all'], __('Delete all spams')))->class('delete'),
                        (new Hidden('ts', (string) time())),
                        App::nonce()->formNonce(),
                    ]),
                ];
            }

            $note = [];
            if ($moderationTTL >= 0) {
                $note = [
                    (new Para())->class('form-note')->items([
                        new Text(
                            null,
                            sprintf(__('All spam comments older than %s day(s) will be automatically deleted.'), $moderationTTL)
                        ),
                        new Text(
                            null,
                            sprintf(
                                __('You can modify this duration in the %s'),
                                (new Link())->href(App::backend()->url()->get('admin.blog.pref') . '#params.antispam_params')->text(__('Blog settings'))->render()
                            )
                        ),
                    ]),
                ];
            }

            $infos = [
                (new Fieldset())
                ->legend(new Legend(__('Information')))
                ->items([
                    (new Ul())
                        ->class([
                            'spaminfo',
                            'clear',     // Needed because of float left on above legend
                        ])
                        ->items([
                            (new Li())->class('spamcount')->items([
                                (new Link())
                                    ->href(App::backend()->url()->get('admin.comments', ['status' => '-2']))
                                    ->text(__('Junk comments:')),
                                (new Strong(' ' . $spam_count)),
                            ]),
                            (new Li())->class('hamcount')->items([
                                (new Link())
                                    ->href(App::backend()->url()->get('admin.comments', ['status' => '1']))
                                    ->text(__('Published comments:')),
                                (new Text(null, ' ' . $published_count)),
                            ]),
                        ]),
                    ...$action,
                    ...$note,
                ]),
            ];
            $group = $spam_count > 0 ? (new Form('info'))->action(App::backend()->getPageURL())->method('post') : (new Div('info'));
            echo $group
                ->items($infos)
            ->render();

            // Filters
            if (!empty($_GET['upd'])) {
                App::backend()->notices()->success(__('Filters configuration has been successfully saved.'));
            }

            $rows = [];
            $i    = 1;
            foreach (self::$filters as $fid => $f) {
                if ($f->hasGUI() && $f->guiURL() !== false) {
                    $gui_url  = $f->guiURL();
                    $gui_link = (new Link())
                        ->href(Html::escapeHTML($gui_url))
                        ->title(__('Filter configuration'))
                        ->text(
                            (new Img('images/edit.svg'))
                                ->alt(__('Filter configuration'))
                                ->class(['mark', 'mark-edit', 'light-only'])
                            ->render() .
                            (new Img('images/edit-dark.svg'))
                                ->alt(__('Filter configuration'))
                                ->class(['mark', 'mark-edit', 'dark-only'])
                            ->render()
                        );
                } else {
                    $gui_link = (new Text(null, '&nbsp;'));
                }

                $rows[] = (new Tr('f_' . $fid))
                    ->class(['line', $f->active ? '' : ' offline'])
                    ->items([
                        (new Td())
                            ->class(App::auth()->prefs()->get('accessibility')->getBool('nodragdrop') ? '' : 'handle')
                            ->items([
                                (new Number(['f_order[' . $fid . ']'], 1, count(self::$filters), $i))
                                    ->class('position')
                                    ->title(__('position')),
                            ]),
                        (new Td())
                            ->class('nowrap')
                            ->items([
                                (new Checkbox(['filters_active[]'], $f->active))
                                    ->value($fid)
                                    ->title(__('Active')),
                            ]),
                        (new Td())
                            ->class('nowrap')
                            ->items([
                                (new Checkbox(['filters_auto_del[]'], $f->auto_delete))
                                    ->value($fid)
                                    ->title(__('Auto Del.')),
                            ]),
                        (new Td())
                            ->class('nowrap')
                            ->text($f->name),
                        (new Td())
                            ->class('maximal')
                            ->text($f->description),
                        (new Td())
                            ->class('status')
                            ->items([$gui_link]),
                    ])
                ;
                $i++;
            }

            echo (new Form('filters-list-form'))
                ->action(App::backend()->getPageURL())
                ->method('post')
                ->fields([
                    (new Fieldset())->legend(new Legend(__('Available spam filters')))->items([
                        (new Div())->class('table-outer')->items([
                            (new Table())
                                ->class(['filters', 'dragable'])
                                ->thead((new Thead())->rows([
                                    (new Th())->text(__('Order')),
                                    (new Th())->text(__('Active')),
                                    (new Th())->text(__('Auto Del.')),
                                    (new Th())->class('nowrap')->text(__('Filter name')),
                                    (new Th())->text(__('Description')),
                                    (new Th())->text(__('Settings')),
                                ]))
                                ->tbody((new Tbody('filters-list'))->rows($rows)),
                        ]),
                        (new Para())->class('form-buttons')->items([
                            (new Hidden('filters_order', '')),
                            (new Submit(['filters_upd'], __('Save'))),
                            (new Button('back', __('Back')))->class(['go-back', 'reset', 'hidden-if-no-js']),
                            App::nonce()->formNonce(),
                        ]),
                    ]),
                ])
            ->render();

            // Syndication
            if (App::config()->adminUrl() !== '') {
                $ham_feed = App::blog()->url() . App::url()->getURLFor(
                    'hamfeed',
                    Antispam::getUserCode()
                );
                $spam_feed = App::blog()->url() . App::url()->getURLFor(
                    'spamfeed',
                    Antispam::getUserCode()
                );

                echo (new Div())->class('fieldset')->items([
                    (new Text('h3', __('Syndication'))),
                    (new Ul())->class('spaminfo')->items([
                        (new Li())->class('feed')->items([
                            (new Link())->href($spam_feed)->text(__('Junk comments RSS feed')),
                        ]),
                        (new Li())->class('feed')->items([
                            (new Link())->href($ham_feed)->text(__('Published comments RSS feed')),
                        ]),
                    ]),
                ])
                ->render();
            }

            App::backend()->page()->helpBlock('antispam', 'antispam-filters');
        }

        App::backend()->page()->closeModule();
    }
}
