<?php

namespace Pages\Helper;

use Closure;

class Pages extends \Lime\Helper {

    protected $locales;

    public function initialize() {
        $this->locales = $this->app->helper('locales')->locales();
    }

    public function remove($id) {

        $ids = [$id];

        $pages = $this->app->dataStorage->find('pages', [
            'filter' => ['_pid' => $id, '_state' => ['$gt' => -1]]
        ])->toArray();

        foreach ($pages as $page) {
            $this->remove($page['_id']);
            $ids[] = $page['_id'];
        }

        $this->app->dataStorage->remove('pages', ['_id' => $id]);
        $this->app->trigger('pages.remove', [$ids]);

        return true;
    }

    public function updateRoutes($pageId, $parent = null) {

        $page = is_array($pageId) && isset($pageId['_id'])
                ? $pageId
                : $this->app->dataStorage->findOne('pages', ['_id' => $pageId]);

        if (!$page) {
            return;
        }

        if ($page['_pid']) {

            $parent = $parent ?? $this->app->dataStorage->findOne('pages', ['_id' => $page['_pid']]);

            if ($parent) {
                $r = $parent['_r'].'/'.$page['slug'];
            }

        } else {
            $r = '/'.$page['slug'];
        }

        // check if route already exists
        if ($this->app->dataStorage->findOne('pages', ['_r' => $r, '_id' => ['$ne' => $page['_id']]], ['_id' => 1])) {
            $r .= '-'.uniqid();
        }

        $data = ['_id' => $page['_id'], '_r' => $r];

        foreach ($this->locales as $locale) {

            if ($locale['i18n'] == 'default') continue;

            $routeKey = "_r_{$locale['i18n']}";
            $slug = $page["slug_{$locale['i18n']}"] ? $page["slug_{$locale['i18n']}"] : $page["slug"];

            if ($page['_pid']) {
                $_r = ($parent["_r_{$locale['i18n']}"] ? $parent["_r_{$locale['i18n']}"] : $parent["_r"]).'/'.$slug;
            } else {
                $_r = '/'.$slug;
            }

            // check if route already exists
            if ($this->app->dataStorage->findOne('pages', [$routeKey => $_r, '_id' => ['$ne' => $page['_id']]], ['_id' => 1])) {
                $_r .= '-'.uniqid();
            }

            $data[$routeKey] = $_r;
        }

        $this->app->dataStorage->save('pages', $data);

        $page = \array_merge($page, $data);

        $children = $this->app->dataStorage->find('pages', [
            'filter' => ['_pid' => $page['_id']]
        ])->toArray();

        if (!count($children)) {
            return;
        }

        foreach ($children as $child) {

            if (!$child['slug']) continue;

            $data = ['_id' => $child['_id'], '_r' => $r.'/'.$child['slug']];

            $this->app->dataStorage->save('pages', $data);

            $child = \array_merge($child, $data);

            $this->updateRoutes($child, $page);
            //$this->updateRoutes($child['_id']);
        }
    }

    public function processPage(array $page, $options = []) {

        $populate = intval($options['populate'] ?? 0);
        $locale = $options['locale'] ?? 'default';
        $locales = $this->app->helper('locales')->locales(true);
        $settings = $this->app->module('pages')->settings();

        if ($locale != 'default' && !isset($locales[$locale])) {
            $locale = 'default';
        }

        // alternative locale routes
        $routes = [];

        foreach ($locales as $l => $meta) {
            $rKey = $l == 'default' ? '_r' : "_r_{$l}";
            if (
                !isset($page[$rKey]) ||
                !$page[$rKey] ||
                (isset($settings['locales'][$l]) && $settings['locales'][$l] === false) ||
                (isset($page['_locales'][$l]) && $page['_locales'][$l] === false)
            ) continue;
            $routes[$l] = $page[$rKey];
        }

        $page['_locale'] = $locale;
        $page['_routes'] = $routes;
        $page['_parents'] = [];

        $page = $this->processType($page, $locale, $options);
        $page = $this->app->helper('locales')->applyLocales($page, $locale);

        // apply default seo
        $seo = $settings[$locale == 'default' ? 'seo' : "seo_{$locale}"] ?? [];
        $page['seo'] = array_merge($page['seo'], array_filter($seo), array_filter($page['seo']));

        if ($page['_pid']) {

            $pid = $page['_pid'];

            while (true) {

                $parent = $this->app->dataStorage->findOne('pages', ['_id' => $pid], ['data' => 0]);
                $pid = $parent['_pid'];

                array_unshift($page['_parents'] , $parent);

                if (!$pid) break;
            }

            if (count($page['_parents'])) {
                $page['_parents'] = $this->app->helper('locales')->applyLocales($page['_parents'], $locale);
            }
        }

        // process possible layout components
        $page = $this->app->helper('layoutComponents')->process($page, ['locale' => $locale, 'populate' => $options['populate'] ?? 0]);

        // resolve linked content items
        if ($populate && $this->app->module('content')) {
            $page = $this->app->module('content')->populate($page, $populate, 0, ['locale' => $locale]);
        }

        // resolve internal page urls
        if ($populate && isset($page['data']) && is_array($page['data'])) {
            $page['data'] = $this->resolveNestedPageUrls($page['data']);
        }

        $this->app->trigger('pages.process.page', [&$page]);

        return $page;
    }

    public function resolveRouteByPageType(string $route, string $locale = 'default', array $options = []) {

        $routeKey = '_r';

        if ($locale && $locale != 'default') {
            $routeKey = "_r_{$locale}";
        }

        $nearest = null;
        $page = null;
        $parts = explode('/', $route);
        $params = [];

        $params[] = array_pop($parts);

        while (count($parts)) {

            $r = implode('/', $parts);
            $f = null;

            if (!$r) {
                $f = ['_pid' => null, '_state' => 1, '_o' => 0];
            } else {
                $f = ["{$routeKey}" => $r, '_state' => 1];
            }

            $nearest = $this->app->dataStorage->findOne('pages', $f);

            if ($nearest) {
                break;
            }

            $params[] = array_pop($parts);
        }

        if ($nearest) {

            $params = array_reverse($params);
            $pageType = $this->module('pages')->pageTypes($nearest['type']);

            if (isset($pageType['resolve']) && \is_callable($pageType['resolve'])) {
                $callback = Closure::bind($pageType['resolve'], $this->app, $this->app);
                $p = $callback($nearest, compact('route', 'locale', 'params', 'options'));
                if ($p) $page = $p;
            }
        }

        return $page;
    }

    public function processType(array $page, string $locale = 'default', array $options = []) {

        $pageType = $this->app->module('pages')->pageTypes($page['type']);

        if (!$pageType) {
            return $page;
        }

        if (isset($pageType['process'])) {
            $callback = Closure::bind($pageType['process'], $this->app, $this->app);
            $page = $callback($page, $locale, $options);
        }

        return $page;
    }

    public function resolveNestedPageUrls(array $array) {

        foreach ($array as $k => &$v) {

            if (is_string($v) && \str_starts_with($v, 'pages://')) {
                $array[$k] = $this->resolvePageUrl($v);
            }

            if (is_string($v) && str_starts_with($v, 'assets://')) {
                $array[$k] = rtrim($this->fileStorage->getURL('root://'), '/').'/assets/link/'.substr($v, 9);
            }

            if (is_array($array[$k])) {
                $array[$k] = $this->resolveNestedPageUrls($array[$k]);
            }
        }

        return $array;
    }

    public function resolvePageUrl(string $url) {

        static $cache;

        if (!$cache) {
            $cache = [];
        }

        $uri = parse_url($url);

        if (!$uri || $uri['scheme'] != 'pages' || !isset($uri['host'])) {
            return null;
        }

        if (isset($uri['query']) && $uri['query']) {
            parse_str($uri['query'], $uri['query']);
        } else {
            $uri['query'] = [];
        }

        $locale = $uri['query']['locale'] ?? 'default';
        $pageId = $uri['host'];

        unset($uri['query']['locale']);

        if (!isset($cache[$pageId])) {
            $cache[$pageId] = $this->app->dataStorage->findOne('pages', ['_id' => $pageId], ['data' => 0, '_meta' => 0]);
        }

        if (!isset($cache[$pageId])) {
            return null;
        }

        $page = $cache[$pageId];

        if ($locale != 'default') {
            $page = $this->app->helper('locales')->applyLocales($page, $locale);
        }

        $route = $page['_r'];

        if (count($uri['query'])) {
            $route .= '?'.http_build_query($uri['query']);
        }

        $parts = ['route' => $route, 'locale' => $locale];

        if (isset($uri['fragment']) && $uri['fragment']) {
            $parts['fragment'] = $uri['fragment'];
        }

        return $parts;
    }

    public function filterInactiveMenuLinks(array $links): array {

        $filtered = [];

        foreach ($links as $link) {

            if (!isset($link['active']) || !$link['active']) continue;

            if (isset($link['children']) && is_array($link['children'])) {
                $link['children'] = $this->filterInactiveMenuLinks($link['children']);
            }

            $filtered[] = $link;
        }

        return $filtered;
    }

}
