<?php

namespace Shm\ShmUtils\Terms;

use Shm\ShmCmd\Cmd;
use Shm\ShmUtils\Response;
use Shm\ShmUtils\ShmInit;
use Shm\ShmUtils\ShmTwig;

/**
 * Public terms pages from Markdown files in {projectRoot}/terms/.
 *
 * Example layout:
 *   GET /terms — list of directories (categories) and root .md files
 *   terms/terms-of-use.md           → GET /terms/terms-of-use
 *   terms/legal/privacy-policy.md   → GET /terms/legal/privacy-policy
 *
 * @see TermsCatalog
 */
class TermsRoute
{
    public static function init(): void
    {
        if (Cmd::cli()) {
            return;
        }

        $f3 = \Base::instance();

        $f3->route('GET /terms/api', function ($f3) {
            self::handleApi();
        });

        $f3->route('GET /terms/pdf', function ($f3) {
            self::handlePdf($f3);
        });

        $f3->route('GET /terms', function ($f3) {
            self::handleIndex($f3);
        });

        $f3->route('GET /terms/*', function ($f3) {
            self::handlePath($f3);
        });
    }

    private static function handlePdf(\Base $f3): void
    {
        if (!TermsCatalog::exists()) {
            $f3->error(404);
            return;
        }

        $pathSegments = self::pdfPathSegmentsFromQuery();
        $entries = TermsCatalog::collectPdfEntries($pathSegments);

        if ($entries === null || $entries === []) {
            $f3->error(404);
            return;
        }

        $labels = self::labels();
        $title = $pathSegments === []
            ? $labels['indexTitle']
            : ($pathSegments[array_key_last($pathSegments)] ?? $labels['indexTitle']);
        $pdf = TermsPdf::render($title, $entries);
        $filename = self::pdfFilename($pathSegments);

        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        http_response_code(200);
        echo $pdf;
        exit;
    }

    /**
     * @return list<string>
     */
    private static function pdfPathSegmentsFromQuery(): array
    {
        $path = isset($_GET['path']) ? trim(rawurldecode((string) $_GET['path']), '/') : '';
        if ($path === '') {
            return [];
        }

        $parts = array_values(array_filter(explode('/', $path), fn (string $p) => $p !== ''));

        return array_map([TermsCatalog::class, 'decodePathSegment'], $parts);
    }

    /**
     * @param list<string> $pathSegments
     */
    private static function pdfFilename(array $pathSegments): string
    {
        if ($pathSegments === []) {
            return 'terms.pdf';
        }

        $name = $pathSegments[array_key_last($pathSegments)] ?? 'terms';
        $name = preg_replace('/[^a-zA-Z0-9._\-]+/u', '-', $name) ?? 'terms';
        $name = trim($name, '-.');
        if ($name === '') {
            $name = 'terms';
        }

        return $name . '.pdf';
    }

    /**
     * @param list<string> $pathSegments
     */
    private static function pdfUrlForPath(array $pathSegments): ?string
    {
        if (!TermsCatalog::hasPdfEntries($pathSegments === [] ? null : $pathSegments)) {
            return null;
        }

        if ($pathSegments === []) {
            return '/terms/pdf';
        }

        $encoded = implode('/', array_map('rawurlencode', $pathSegments));

        return '/terms/pdf?path=' . $encoded;
    }

    /**
     * Path segments after /terms from a page URL.
     *
     * @return list<string>
     */
    private static function pathSegmentsFromUrl(string $url): array
    {
        $url = trim($url);
        if ($url === '/terms' || $url === '') {
            return [];
        }

        if (!str_starts_with($url, '/terms/')) {
            return [];
        }

        $tail = trim(substr($url, strlen('/terms/')), '/');
        if ($tail === '') {
            return [];
        }

        $parts = array_values(array_filter(explode('/', $tail), fn (string $p) => $p !== ''));

        return array_map([TermsCatalog::class, 'decodePathSegment'], $parts);
    }

    private static function apiTreeOnly(): bool
    {
        if (!isset($_GET['tree'])) {
            return false;
        }

        return filter_var($_GET['tree'], FILTER_VALIDATE_BOOLEAN);
    }

    private static function handleApi(): void
    {
        if (!TermsCatalog::exists()) {
            Response::json(['success' => false, 'error' => 'NOT_FOUND', 'message' => 'Terms not found'], 404);
        }

        $labels = self::labels();
        $q = isset($_GET['q']) ? trim(rawurldecode((string) $_GET['q'])) : '';
        $treeOnly = self::apiTreeOnly();

        if ($q !== '') {
            $tree = TermsCatalog::findApiTreeByQuery($q, $treeOnly);
            Response::json([
                'success' => true,
                'tree' => $tree,
            ]);
        }

        $tree = TermsCatalog::buildApiTree($treeOnly);
        $tree['title'] = $labels['indexTitle'];

        Response::json([
            'success' => true,
            'tree' => $tree,
        ]);
    }

    private static function handlePath(\Base $f3): void
    {
        if (!TermsCatalog::exists()) {
            $f3->error(404);
            return;
        }

        $params = $f3->get('PARAMS') ?? [];
        $tail = $params['*'] ?? '';
        if (is_array($tail)) {
            $tail = implode('/', $tail);
        }
        $tail = trim((string) $tail, '/');
        if ($tail === '') {
            $f3->reroute('/terms');
            return;
        }

        $parts = array_values(array_filter(explode('/', $tail), fn (string $p) => $p !== ''));
        $parts = array_map([TermsCatalog::class, 'decodePathSegment'], $parts);

        if (count($parts) === 1) {
            self::handleSlug($parts[0], $f3);
            return;
        }

        $document = TermsCatalog::resolveDocumentByPath($parts);
        if ($document !== null) {
            self::renderDocument($document);
            return;
        }

        $category = TermsCatalog::resolveCategoryByPath($parts);
        if ($category !== null) {
            self::renderCategory($category);
            return;
        }

        $f3->error(404);
    }

    private static function handleIndex(\Base $f3): void
    {
        if (!TermsCatalog::exists()) {
            $f3->error(404);
            return;
        }

        $labels = self::labels();

        $hub = TermsCatalog::hubContents();

        self::respond([
            'pageType' => 'browse',
            'browserTitle' => $labels['indexTitle'],
            'title' => $labels['indexTitle'],
            'contentHtml' => '',
            'currentUrl' => '/terms',
            'categories' => $hub['categories'],
            'browseDocuments' => $hub['documents'],
            'labels' => $labels,
        ]);
    }

    private static function handleSlug(string $slug, \Base $f3): void
    {
        if (!TermsCatalog::exists()) {
            $f3->error(404);
            return;
        }

        $document = TermsCatalog::resolve(null, $slug);
        if ($document !== null) {
            self::renderDocument($document);
            return;
        }

        $category = TermsCatalog::resolveCategoryByPath([$slug]);
        if ($category !== null) {
            self::renderCategory($category);
            return;
        }

        $f3->error(404);
    }

    /**
     * @param array{title: string, url: string, documents: list<array>, categories: list<array>} $category
     */
    private static function renderCategory(array $category): void
    {
        $labels = self::labels();

        self::respond([
            'pageType' => 'browse',
            'browserTitle' => $category['title'],
            'title' => $category['title'],
            'contentHtml' => '',
            'currentUrl' => $category['url'],
            'categories' => $category['categories'],
            'browseDocuments' => $category['documents'],
            'labels' => $labels,
        ]);
    }

    private static function renderDocument(TermsDocument $document): void
    {
        $labels = self::labels();

        self::respond([
            'pageType' => 'document',
            'browserTitle' => $document->title,
            'title' => $document->title,
            'contentHtml' => TermsCatalog::readDocumentHtml($document),
            'currentUrl' => $document->url,
            'labels' => $labels,
        ]);
    }

    /**
     * @return array{lang: string, indexTitle: string, categoriesBlock: string, documentsBlock: string, openMenu: string, closeMenu: string, downloadPdf: string}
     */
    private static function labels(): array
    {
        if (ShmInit::$lang === 'en') {
            return [
                'lang' => 'en',
                'indexTitle' => 'Documents',
                'categoriesBlock' => 'Sections',
                'documentsBlock' => 'Documents',
                'openMenu' => 'Open menu',
                'closeMenu' => 'Close menu',
                'downloadPdf' => 'Download PDF',
            ];
        }

        return [
            'lang' => 'ru',
            'indexTitle' => 'Документы',
            'categoriesBlock' => 'Разделы',
            'documentsBlock' => 'Документы',
            'openMenu' => 'Открыть меню',
            'closeMenu' => 'Закрыть меню',
            'downloadPdf' => 'Скачать PDF',
        ];
    }

    /**
     * @param array<string, mixed> $context
     */
    private static function respond(array $context): void
    {
        $currentUrl = $context['currentUrl'] ?? '/terms';
        $context['nav'] = TermsCatalog::buildNav($currentUrl);
        $context['labels'] = $context['labels'] ?? self::labels();
        $context['pdfUrl'] = self::pdfUrlForPath(self::pathSegmentsFromUrl($currentUrl));

        $html = ShmTwig::render('@shm/terms', $context);
        Response::html($html);
    }
}
