<?php

namespace Detektivo\Helper;

use Closure;

class Indexer extends \Lime\Helper {

    protected string $name;
    protected ?string $runId = null;

    public static function init(string $name, $app) {
        $indexer = new static($name, $app);
    }

    public function __construct(string $name, $app) {
        parent::__construct($app);

        $this->name = $name;
    }

    public function start(array $meta = []): mixed {
        $detektivo = $this->app->module('detektivo');
        $this->runId = isset($meta['_runId']) && \is_string($meta['_runId']) ? $meta['_runId'] : null;
        unset($meta['_runId']);

        if (!$detektivo->exists($this->name)) {
            return null;
        }

        if (!$detektivo->beginIndexer($this->name, ['runId' => $this->runId])) {
            return null;
        }

        $index = $detektivo->indexes($this->name);
        $type = $detektivo->indexTypes($index['type']);

        if (!$type || !isset($type['index']) || !\is_callable($type['index'])) {
            $detektivo->finishIndexer($this->name, 'failed', ['error' => 'Missing index callback']);
            return null;
        }

        $index['meta'] = \array_replace_recursive($index['meta'] ?? [], $meta);

        $idx = $detektivo->index($this->name);

        $callback = Closure::bind($type['index'], $this, $this);

        // reset any stale entries from previous runs
        $this->app->dataStorage->setKey('detektivo/indexer', $this->name, []);

        try {

            $this->log('Indexing started');

            $callback($index, $idx);

            if ($this->isIndexerRunning()) {
                $this->log('Indexing finished');
                $detektivo->finishIndexer($this->name, 'finished');
            }

        } catch (\Throwable $e) {
            $this->log("Indexing failed: {$e->getMessage()}", 'error');
            $detektivo->finishIndexer($this->name, 'failed', ['error' => $e->getMessage()]);
            throw $e;
        }

        return true;
    }

    public function isIndexerRunning(): bool {
        return $this->app->module('detektivo')->isIndexerRunning($this->name);
    }

    public function heartbeat(array $meta = []): array {

        if ($this->runId) {
            $meta['runId'] = $this->runId;
        }

        return $this->app->module('detektivo')->touchIndexer($this->name, $meta);
    }

    public function log(string $msg, string $type = 'info') {
        $now = \time();
        $this->app->dataStorage->rpush('detektivo/indexer', $this->name, ['time' => $now, 'msg' => $msg, 'type' => $type]);
        $this->heartbeat(['lastLogTime' => $now]);
    }

    public function getLog(): ?array {
        return $this->app->dataStorage->getKey('detektivo/indexer', $this->name, []);
    }
}
