<?php

namespace OFFLINE\SiteSearch\Classes\Providers;

use October\Rain\Halcyon\Collection;
use OFFLINE\SiteSearch\Classes\Result;
use OFFLINE\SiteSearch\Models\Settings;
use RainLab\Pages\Classes\Page;

/**
 * Searches the contents generated by the
 * Rainlab.Pages plugin
 *
 * @package OFFLINE\SiteSearch\Classes\Providers
 */
class RainlabPagesResultsProvider extends ResultsProvider
{
    /**
     * Runs the search for this provider.
     *
     * @return ResultsProvider
     */
    public function search()
    {
        if ( ! $this->isInstalledAndEnabled()) {
            return $this;
        }

        foreach ($this->pages() as $page) {
            // Make this result more relevant, if the query is found in the title
            $title     = $page->viewBag['meta_title'] ?? $page->viewBag['title'] ?? '';
            $relevance = $this->containsQuery($title) ? 2 : 1;

            $result        = new Result($this->query, $relevance);
            $result->title = $title;
            $result->text  = $page->parsedMarkup;
            $result->url   = $this->getUrl($page);
            $result->model = $page;

            $this->addResult($result);
        }

        return $this;
    }

    /**
     * Get all pages with matching title or content.
     *
     * @return Collection
     */
    protected function pages()
    {
        return Page::all()->filter(function ($page) {
            $viewBag  = $page->viewBag;
            $isHidden = isset($viewBag['is_hidden']) ? (bool)$viewBag['is_hidden'] : false;

            if ($isHidden) {
                return false;
            }

            try {
                return $this->containsQuery($page->parsedMarkup)
                    || $this->containsQuery($page->placeholders)
                    || $this->viewBagContainsQuery($viewBag);
            } catch(\Throwable $e) {
                // If an exception was thrown chances are that a page contained invalid markup.
                return false;
            }
        });
    }

    /**
     * Checks if the RainLab.Pages Plugin is installed and
     * enabled in the config.
     *
     * @return bool
     */
    protected function isInstalledAndEnabled()
    {
        return $this->isPluginAvailable($this->identifier)
            && Settings::get('rainlab_pages_enabled', true);
    }

    /**
     * Checks if $subject contains the query string.
     *
     * @param $subject
     *
     * @return bool
     */
    protected function containsQuery($subject)
    {
        return is_array($subject)
            ? $this->arrayContainsQuery($subject)
            : mb_strpos(mb_strtolower($subject), mb_strtolower($this->query)) !== false;
    }

    /**
     * Checks if a viewBag contains the query string.
     *
     * @param $viewBag
     *
     * @return bool
     */
    protected function viewBagContainsQuery($viewBag)
    {
        $ignoreViewBagKeys = [
            'url',
            'layout',
            'is_hidden',
            'navigation_hidden',
        ];

        $properties = collect($viewBag)->except($ignoreViewBagKeys)->toArray();

        return $this->arrayContainsQuery($properties);
    }

    /**
     * Checks if an array contains the query string.
     *
     * @param $array
     *
     * @return bool
     */
    protected function arrayContainsQuery(array $array)
    {
        foreach ($array as $value) {
            if ($this->containsQuery($value)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Display name for this provider.
     *
     * @return string
     */
    public function displayName()
    {
        return Settings::get('rainlab_pages_label', 'Page');
    }

    /**
     * Get the page's (translated) url.
     *
     * @param $page
     *
     * @return string
     */
    protected function getUrl($page)
    {
        $langPrefix = $this->translator ? $this->translator->getLocale() : '';

        return $langPrefix . $page->viewBag['url'];
    }

    /**
     * Returns the plugin's identifier string.
     *
     * @return string
     */
    public function identifier()
    {
        return 'RainLab.Pages';
    }
}
