<?PHP
/**
 * Contains a class for keeping the source fulltext index in sync.
 *
 * @author Joshua Ramon Enslin <joshua@museum-digital.de>
 */
declare(strict_types = 1);

/**
 * Class for keeping the source fulltext index in sync.
 */
final class NodaSourceFulltextSync {

    /** @var MDMysqli */
    private MDMysqli $_mysqli_noda;

    /**
     * Removes the obsolete entries.
     *
     * @return void
     */
    public function removeObsolete():void {

        $result = $this->_mysqli_noda->do_read_query("SELECT 1
            FROM `source_fulltext_search`
            WHERE NOT EXISTS (SELECT 1 FROM `source`
                WHERE `source`.`source_id` = `source_fulltext_search`.`source_id`)
            LIMIT 1");

        if ($result->num_rows === 0) {
            $result->close();
            return;
        }
        $result->close();

        $this->_mysqli_noda->do_update_query_large("DELETE FROM `source_fulltext_search`
            WHERE NOT EXISTS (SELECT 1 FROM `source`
                WHERE `source`.`source_id` = `source_fulltext_search`.`source_id`)");

    }

    /**
     * Gets the entries to update.
     *
     * @return array<string>
     */
    public function getNewEntries():array {

        $result = $this->_mysqli_noda->do_read_query("SELECT `source_id`,
                `source_title`, `source_year`, `source_booktitle`, `source_venue`,
                `source_volume`, `source_issue`, `source_pages`, `source_url`,
                `source_doi`, `source_isbn`, `source_license`
            FROM `source`
            WHERE NOT EXISTS (SELECT 1
                FROM `source_fulltext_search`
                WHERE `source`.`source_id` = `source_fulltext_search`.`source_id`
                    AND `source`.`source_update_timestamp` <= `source_fulltext_search`.`update_timestamp`)");

        if ($result->num_rows === 0) {
            $result->close();
            return [];
        }

        $toUpdate = [];

        while ($cur = $result->fetch_assoc()) {
            $cur['authors'] = "";
            $source_id = (int)$cur['source_id'];
            unset($cur['source_id']);
            $toUpdate[$source_id] = $cur;
        }
        $result->close();

        $authorsResult = $this->_mysqli_noda->do_read_query("SELECT `source_author`.`source_id`, `persinst_anzeigename` AS `name`
            FROM `persinst`, `source_author`
            WHERE `source_author`.`author_id` = `persinst`.`persinst_id`
                AND `source_author`.`source_id` IN (" . implode(', ', array_keys($toUpdate)) . ")");

        $authors = [];
        while ($cur = $authorsResult->fetch_assoc()) {
            $source_id = (int)$cur['source_id'];
            if (empty($authors[$source_id])) {
                $authors[$source_id] = [$cur['name']];
            }
            else $authors[$source_id][] = $cur['name'];
        }
        $authorsResult->close();

        $newEntries = [];

        foreach ($toUpdate as $source_id => $values) {
            $cur = implode(' ', $values);
            if (!empty($authors[$source_id])) $cur .= ' ' . implode(' ', array_unique($authors[$source_id]));
            $newEntries[$source_id] = $cur;
        }

        return $newEntries;

    }

    /**
     * Loads the list of entries to update.
     *
     * @return void
     */
    public function update():void {

        if (empty($toUpdate = $this->getNewEntries())) {
            return;
        }

        if (count($toUpdate) > 1) {
            $this->_mysqli_noda->autocommit(false);
        }

        $updateStmt = $this->_mysqli_noda->do_prepare("INSERT INTO `source_fulltext_search`
            (`source_id`, `content`, `update_timestamp`)
            VALUES
            (?, ?, NOW())
            ON DUPLICATE KEY UPDATE `content` = ?,
                `update_timestamp` = NOW()");

        foreach ($toUpdate as $key => $value) {
            $updateStmt->bind_param("iss", $key, $value, $value);
            $updateStmt->execute();
        }

        $updateStmt->close();

        if (count($toUpdate) > 1) {
            $this->_mysqli_noda->commit();
            $this->_mysqli_noda->autocommit(true);
        }

    }

    /**
     * Sync: Wrapper function glueing together the sync process.
     *
     * @return void
     */
    public function sync():void {

        $this->removeObsolete();
        $this->update();

    }

    /**
     * Constructor.
     *
     * @param MDMysqli $mysqli_noda DB connection.
     *
     * @return void
     */
    public function __construct(MDMysqli $mysqli_noda) {

        $this->_mysqli_noda = $mysqli_noda;

    }
}
