<?php

declare(strict_types=1);

namespace PpPhp\Plugins;

use PpPhp\Core\EditPlugin;
use PpPhp\Core\Html\Sanitizer;
use PpPhp\Core\PluginSupport;

/**
 * Self-contained WYSIWYG rich-text editor (no external library): a small toolbar
 * over a contenteditable surface, synced into a hidden input. The stored value is
 * HTML — the initial content is SANITISED before it enters the editor, and it must
 * be displayed through the "HTML" view (which sanitises again). Toolbar covers
 * bold/italic/underline, H1/H2, lists, link and clear-formatting.
 */
class RichText implements EditPlugin
{
    use PluginSupport;

    public static function id(): string { return 'RichText'; }
    public static function label(): string { return 'Rich Text (WYSIWYG)'; }
    public static function version(): string { return '1.0.0'; }
    public static function description(): string { return 'A lightweight WYSIWYG editor (bold, headings, lists, links) storing sanitised HTML. Display with the HTML view.'; }

    public static function render(string $fieldName, mixed $value, array $settings): string
    {
        $name = self::e($fieldName);
        // The editor never loads unsanitised HTML.
        $safe = Sanitizer::clean((string) ($value ?? ''));
        $minH = (int) ($settings['minHeight'] ?? 8);
        if ($minH < 3) $minH = 3;

        $html = '<div class="pp-rte" data-pp-rte>'
            . '<div class="pp-rte-tb" role="toolbar" aria-label="Formatting">'
            . self::btn('bold', 'B', 'Bold', 'font-weight:700')
            . self::btn('italic', 'I', 'Italic', 'font-style:italic')
            . self::btn('underline', 'U', 'Underline', 'text-decoration:underline')
            . '<span class="pp-rte-div"></span>'
            . self::btn('h1', 'H1', 'Heading 1', '')
            . self::btn('h2', 'H2', 'Heading 2', '')
            . self::btn('p', '¶', 'Paragraph', '')
            . '<span class="pp-rte-div"></span>'
            . self::btn('ul', '&bull;', 'Bulleted list', '')
            . self::btn('ol', '1.', 'Numbered list', '')
            . self::btn('link', '&#128279;', 'Insert link', '')
            . self::btn('unlink', '&#128683;', 'Remove link', '')
            . '<span class="pp-rte-div"></span>'
            . self::btn('clear', '&#10005;', 'Clear formatting', '')
            . '</div>'
            . '<div class="pp-rte-area" contenteditable="true" style="min-height:' . $minH . 'em">' . $safe . '</div>'
            . '<input type="hidden" name="' . $name . '" class="pp-rte-val" value="' . self::e($safe) . '">'
            . '</div>';

        return $html . self::enhancer();
    }

    private static function btn(string $cmd, string $glyph, string $title, string $style): string
    {
        $st = $style !== '' ? ' style="' . $style . '"' : '';
        return '<button type="button" class="pp-rte-b" data-cmd="' . $cmd . '" title="' . self::e($title) . '" aria-label="' . self::e($title) . '"' . $st . '>' . $glyph . '</button>';
    }

    public static function settingsSchema(): array
    {
        return [['key' => 'minHeight', 'type' => 'number', 'label' => 'Min height (lines)', 'default' => 8]];
    }

    public static function assets(): array
    {
        return ['js' => [], 'css' => ['plugins/Edit/RichText/richtext.css']];
    }

    private static function enhancer(): string
    {
        return <<<'HTML'
<script>
(function(){
  if (!window.__ppRteInit) {
    window.__ppRteInit = function(root){
      (root || document).querySelectorAll('.pp-rte').forEach(function(box){
        if (box._ppRte) return; box._ppRte = true;
        var area = box.querySelector('.pp-rte-area');
        var val  = box.querySelector('.pp-rte-val');
        if (!area || !val) return;
        function safeUrl(u){ u = (u||'').trim(); if (!u) return ''; if (/^\s*(javascript|data|vbscript):/i.test(u.replace(/[\x00-\x20]/g,''))) return ''; return u; }
        function scrub(){
          area.querySelectorAll('script,style,iframe,object,embed').forEach(function(n){ n.remove(); });
          area.querySelectorAll('*').forEach(function(el){
            for (var i = el.attributes.length - 1; i >= 0; i--) {
              var a = el.attributes[i].name;
              if (/^on/i.test(a) || (a === 'href' && !safeUrl(el.getAttribute('href'))) || (a === 'src')) el.removeAttribute(a);
            }
          });
        }
        function sync(){ scrub(); val.value = area.innerHTML; }
        function exec(cmd){
          area.focus();
          if (cmd === 'bold' || cmd === 'italic' || cmd === 'underline') document.execCommand(cmd, false, null);
          else if (cmd === 'h1') document.execCommand('formatBlock', false, 'H1');
          else if (cmd === 'h2') document.execCommand('formatBlock', false, 'H2');
          else if (cmd === 'p')  document.execCommand('formatBlock', false, 'P');
          else if (cmd === 'ul') document.execCommand('insertUnorderedList', false, null);
          else if (cmd === 'ol') document.execCommand('insertOrderedList', false, null);
          else if (cmd === 'unlink') document.execCommand('unlink', false, null);
          else if (cmd === 'clear') document.execCommand('removeFormat', false, null);
          else if (cmd === 'link') { var u = safeUrl(window.prompt('Link URL (http/https/mailto):', 'https://')); if (u) document.execCommand('createLink', false, u); }
          sync();
        }
        box.querySelectorAll('.pp-rte-b').forEach(function(b){
          b.addEventListener('mousedown', function(e){ e.preventDefault(); });   // keep selection
          b.addEventListener('click', function(e){ e.preventDefault(); exec(b.getAttribute('data-cmd')); });
        });
        area.addEventListener('input', sync);
        area.addEventListener('blur', sync);
      });
    };
  }
  if (document.readyState !== 'loading') window.__ppRteInit();
  else document.addEventListener('DOMContentLoaded', function(){ window.__ppRteInit(); });
})();
</script>
HTML;
    }
}
