<?php

namespace app\foreignone\files;

use engine\SphinxUtil;
use Sphinx\SphinxClient;

abstract class Util
{
    /**
     * @param ArchiveType $type
     * @param int[] $ids
     * @param string[] $keywords Must already be lower-cased
     * @param int $before
     * @param int $after
     * @return array
     */
    public static function getTextExcerpts(ArchiveType $type, array $ids, array $keywords, int $before = 50, int $after = 50): array {
        list($table, $field_id) = $type->getMySQLData();

        $results = [];
        foreach ($ids as $id)
            $results[$id] = null;

        $db = getDB();

        $dynamic_sql_parts = [];
        $combined_parts = [];
        foreach ($keywords as $keyword) {
            $part = "LOCATE('".$db->escape($keyword)."', text)";
            $dynamic_sql_parts[] = $part;
        }
        if (count($dynamic_sql_parts) > 1) {
            foreach ($dynamic_sql_parts as $part)
                $combined_parts[] = "IF({$part} > 0, {$part}, CHAR_LENGTH(text) + 1)";
            $combined_parts = implode(', ', $combined_parts);
            $combined_parts = 'LEAST('.$combined_parts.')';
        } else {
            $combined_parts = "IF({$dynamic_sql_parts[0]} > 0, {$dynamic_sql_parts[0]}, CHAR_LENGTH(text) + 1)";
        }

        $total = $before + $after;
        $sql = "SELECT
            {$field_id} AS id,
            GREATEST(
                1,
                {$combined_parts} - {$before}
            ) AS excerpt_start_index,
            SUBSTRING(
                text,
                GREATEST(
                    1,
                    {$combined_parts} - {$before}
                ),
                LEAST(
                    CHAR_LENGTH(text),
                    {$total} + {$combined_parts} - GREATEST(1, {$combined_parts} - {$before})
                )
            ) AS excerpt
        FROM
            {$table}
        WHERE
            {$field_id} IN (".implode(',', $ids).")";

        $q = $db->query($sql);
        while ($row = $db->fetch($q)) {
            $results[$row['id']] = [
                'excerpt' => preg_replace('/\s+/', ' ', $row['excerpt']),
                'index' => (int)$row['excerpt_start_index']
            ];
        }

        return $results;
    }

    public static function searchArchive(ArchiveType $type,
                                         string $q,
                                         int $offset,
                                         int $count): array
    {
        $index = $type->getSphinxIndex();
        $query_filtered = SphinxUtil::mkquery($q);

        $cl = SphinxUtil::getClient();
        $cl->setLimits($offset, $count);
        $cl->setMatchMode(SphinxClient::SPH_MATCH_EXTENDED);
        $cl->setRankingMode(SphinxClient::SPH_RANK_PROXIMITY_BM25);

        switch ($type) {
            case ArchiveType::Baconiana:
                $cl->setFieldWeights([
                    'year' => 10,
                    'issues' => 9,
                    'text' => 8
                ]);
                $cl->setSortMode(SphinxClient::SPH_SORT_RELEVANCE);
                break;

            case ArchiveType::MercureDeFrance:
                $cl->setFieldWeights([
                    'date' => 10,
                    'issue' => 9,
                    'text' => 8
                ]);
                $cl->setSortMode(SphinxClient::SPH_SORT_RELEVANCE);
                break;

            case ArchiveType::WilliamFriedman:
                $cl->setFieldWeights([
                    'title' => 50,
                    'document_id' => 60,
                ]);
                $cl->setSortMode(SphinxClient::SPH_SORT_EXTENDED, '@relevance DESC, is_folder DESC');
                break;
        }

        // run search
        $final_query = "$query_filtered";
        $result = $cl->query($final_query, $index);
        $error = $cl->getLastError();
        $warning = $cl->getLastWarning();
        if ($error)
            logError(__METHOD__, $error);
        if ($warning)
            logWarning(__METHOD__, $warning);
        if ($result === false)
            return ['count' => 0, 'items' => []];

        $total_found = (int)$result['total_found'];

        $items = [];
        if (!empty($result['matches']))
            $items = $type->getItemsByIdGetter()(array_keys($result['matches']));

        return ['count' => $total_found, 'items' => $items];
    }

    public static function reindexArchive(ArchiveType $type): void {
        $index = $type->getSphinxIndex();
        SphinxUtil::execute("TRUNCATE RTINDEX $index");

        switch ($type) {
            case ArchiveType::MercureDeFrance:
                foreach (MDFIssue::getAll() as $item) {
                    $text = $item->getFullText();
                    SphinxUtil::execute("INSERT INTO $index (id, volume, issue, date, text) VALUES (?, ?, ?, ?, ?)",
                        $item->id, $item->volume, (string)$item->issue, $item->getHumanFriendlyDate(), $text);
                }
                break;

            case ArchiveType::WilliamFriedman:
                foreach (WFFArchiveFile::getAll() as $item) {
                    $text = $item->getFullText();
                    SphinxUtil::execute("INSERT INTO $index (id, document_id, title, text, is_folder, parent_id) VALUES (?, ?, ?, ?, ?, ?)",
                        $item->id, $item->getDocumentId(), $item->title, $text, (int)($item->type == 'folder'), $item->parentId);
                }
                break;

            case ArchiveType::Baconiana:
                foreach (BaconianaIssue::getList() as $item) {
                    $text = $item->getFullText();
                    if (!$text)
                        continue;
                    BaconianaIssue::addToSphinx($item, $text);
                }
                break;
        }
    }
}