<?php

declare(strict_types=1);

namespace Numinix\SeekmodoSdk\Search;

/**
 * Orchestrates Enhanced Native: normalize → LKG → retrieve → bundle → bias.
 */
final class EnhancedNativeOrchestrator
{
    private QueryNormalizer $normalizer;
    private LkgCache $lkg;
    private FallbackBundle $bundle;
    private SynonymExpander $synonyms;
    private PinApplicator $pins;
    private ZeroResultRescue $rescue;
    private LocalClickBias $clickBias;

    public function __construct(
        QueryNormalizer $normalizer,
        LkgCache $lkg,
        FallbackBundle $bundle,
        ?SynonymExpander $synonyms = null,
        ?PinApplicator $pins = null,
        ?ZeroResultRescue $rescue = null,
        ?LocalClickBias $clickBias = null
    ) {
        $this->normalizer = $normalizer;
        $this->lkg = $lkg;
        $this->bundle = $bundle;
        $this->synonyms = $synonyms ?? new SynonymExpander($bundle);
        $this->pins = $pins ?? new PinApplicator($bundle);
        $this->rescue = $rescue ?? new ZeroResultRescue($bundle);
        $this->clickBias = $clickBias ?? new LocalClickBias();
    }

    /**
     * @param callable(string): array{ids: list<int>, total?: int} $retrieve
     * @return array{ids: list<int>, total: int, source: string, stale: bool}
     */
    public function run(string $q, callable $retrieve): array
    {
        $normalized = $this->normalizer->normalize($q);
        if ($normalized === null) {
            return ['ids' => [], 'total' => 0, 'source' => 'enhanced_native', 'stale' => false];
        }

        $key = $this->normalizer->cacheKey($normalized);
        $lkg = $this->lkg->get($key);
        if ($lkg !== null) {
            $ids = $this->postProcess($normalized, $lkg);
            return [
                'ids' => $ids,
                'total' => count($ids),
                'source' => 'enhanced_native',
                'stale' => true,
            ];
        }

        $expanded = $this->synonyms->expandQuery($normalized);
        $result = $retrieve($expanded);
        $ids = is_array($result['ids'] ?? null) ? $result['ids'] : [];
        $ids = array_values(array_filter(array_map('intval', $ids)));
        $ids = $this->postProcess($normalized, $ids);
        $ids = $this->rescue->rescue($normalized, $ids);
        $ids = $this->clickBias->rerank($normalized, $ids);

        $total = isset($result['total']) ? (int) $result['total'] : count($ids);

        return [
            'ids' => $ids,
            'total' => $total > 0 ? $total : count($ids),
            'source' => 'enhanced_native',
            'stale' => false,
        ];
    }

    public function recordGatewaySuccess(string $q, array $ids): void
    {
        $normalized = $this->normalizer->normalize($q);
        if ($normalized === null || $ids === []) {
            return;
        }
        $this->lkg->store($this->normalizer->cacheKey($normalized), $ids);
    }

    /**
     * @param list<int> $ids
     * @return list<int>
     */
    private function postProcess(string $normalized, array $ids): array
    {
        $ids = array_values(array_filter(array_map('intval', $ids)));
        return $this->pins->apply($normalized, $ids);
    }

    public function getBundle(): FallbackBundle
    {
        return $this->bundle;
    }
}
