<?php

namespace App\Services;

/**
 * Renders Notion blocks as HTML for The Manifest site.
 * Supports the block types Claude generates plus the common ones the
 * dev team uses in task pages (images, code, callouts, tables, toggles).
 */
class NotionRenderer
{
    private const MAX_DEPTH = 3;

    public function __construct(private readonly NotionService $notion) {}

    /**
     * Render a full Notion page body as HTML.
     */
    public function render(string $pageId): string
    {
        return $this->renderBlocks($this->notion->getBlocks($pageId));
    }

    private function renderBlocks(array $blocks, int $depth = 0): string
    {
        $html    = '';
        $listTag = null; // currently open <ul>/<ol>

        foreach ($blocks as $block) {
            $type = $block['type'] ?? '';

            // Group consecutive list items into a single list element
            $wantedTag = match ($type) {
                'bulleted_list_item' => 'ul',
                'numbered_list_item' => 'ol',
                'to_do'              => 'ul',
                default              => null,
            };

            if ($listTag !== null && $wantedTag !== $listTag) {
                $html   .= "</{$listTag}>";
                $listTag = null;
            }

            if ($wantedTag !== null && $listTag === null) {
                $html   .= "<{$wantedTag}>";
                $listTag = $wantedTag;
            }

            $html .= $this->renderBlock($block, $depth);
        }

        if ($listTag !== null) {
            $html .= "</{$listTag}>";
        }

        return $html;
    }

    private function renderBlock(array $block, int $depth): string
    {
        $type = $block['type'] ?? '';
        $data = $block[$type] ?? [];
        $text = $this->richText($data['rich_text'] ?? []);

        $children = '';
        if (($block['has_children'] ?? false) && $depth < self::MAX_DEPTH && $type !== 'table') {
            $children = $this->renderBlocks($this->notion->getBlocks($block['id']), $depth + 1);
        }

        return match ($type) {
            'heading_1'          => "<h2>{$text}</h2>",
            'heading_2'          => "<h3>{$text}</h3>",
            'heading_3'          => "<h4>{$text}</h4>",
            'paragraph'          => $text === '' ? '' : "<p>{$text}</p>{$children}",
            'bulleted_list_item',
            'numbered_list_item' => "<li>{$text}{$children}</li>",
            'to_do'              => '<li class="todo">' . ($data['checked'] ?? false ? '☑ ' : '☐ ') . "{$text}</li>",
            'quote'              => "<blockquote>{$text}{$children}</blockquote>",
            'callout'            => '<div class="callout">' . $this->calloutIcon($data) . "<div>{$text}{$children}</div></div>",
            'code'               => '<pre><code class="language-' . htmlspecialchars($data['language'] ?? 'plain') . '">'
                                     . $this->plainText($data['rich_text'] ?? []) . '</code></pre>',
            'divider'            => '<hr>',
            'image'              => $this->image($data, $text),
            'bookmark'           => $this->link($data['url'] ?? '', $data['url'] ?? ''),
            'link_preview'       => $this->link($data['url'] ?? '', $data['url'] ?? ''),
            'toggle'             => "<details><summary>{$text}</summary>{$children}</details>",
            'table'              => $this->table($block),
            'child_page'         => '', // do not leak links to other Notion pages
            default              => $children,
        };
    }

    /**
     * Render rich text with annotations (bold, italic, code, links).
     */
    private function richText(array $richTexts): string
    {
        $html = '';

        foreach ($richTexts as $rt) {
            $piece = htmlspecialchars($rt['plain_text'] ?? '', ENT_QUOTES, 'UTF-8');
            $ann   = $rt['annotations'] ?? [];

            if ($ann['code'] ?? false)          $piece = "<code>{$piece}</code>";
            if ($ann['bold'] ?? false)          $piece = "<strong>{$piece}</strong>";
            if ($ann['italic'] ?? false)        $piece = "<em>{$piece}</em>";
            if ($ann['strikethrough'] ?? false) $piece = "<s>{$piece}</s>";

            if (! empty($rt['href'])) {
                $href  = htmlspecialchars($rt['href'], ENT_QUOTES, 'UTF-8');
                $piece = "<a href=\"{$href}\" target=\"_blank\" rel=\"noopener\">{$piece}</a>";
            }

            $html .= $piece;
        }

        return $html;
    }

    private function plainText(array $richTexts): string
    {
        return htmlspecialchars(
            implode('', array_map(fn ($rt) => $rt['plain_text'] ?? '', $richTexts)),
            ENT_QUOTES,
            'UTF-8'
        );
    }

    private function image(array $data, string $caption): string
    {
        $url = $data['external']['url'] ?? $data['file']['url'] ?? '';

        if ($url === '') {
            return '';
        }

        $src = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
        $cap = $caption !== '' ? "<figcaption>{$caption}</figcaption>" : '';

        return "<figure><img src=\"{$src}\" alt=\"\" loading=\"lazy\">{$cap}</figure>";
    }

    private function link(string $url, string $label): string
    {
        if ($url === '') {
            return '';
        }

        $href = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');

        return "<p><a href=\"{$href}\" target=\"_blank\" rel=\"noopener\">" .
               htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</a></p>';
    }

    private function calloutIcon(array $data): string
    {
        $emoji = $data['icon']['emoji'] ?? '💡';

        return '<span class="callout-icon">' . htmlspecialchars($emoji, ENT_QUOTES, 'UTF-8') . '</span>';
    }

    private function table(array $block): string
    {
        $rows      = $this->notion->getBlocks($block['id']);
        $hasHeader = $block['table']['has_column_header'] ?? false;
        $html      = '<div class="table-wrap"><table>';

        foreach ($rows as $i => $row) {
            $cells = $row['table_row']['cells'] ?? [];
            $tag   = ($hasHeader && $i === 0) ? 'th' : 'td';
            $html .= '<tr>';
            foreach ($cells as $cell) {
                $html .= "<{$tag}>" . $this->richText($cell) . "</{$tag}>";
            }
            $html .= '</tr>';
        }

        return $html . '</table></div>';
    }
}
