/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.backward_index;

import java.io.ByteArrayInputStream;
import java.nio.file.Path;
import java.util.Base64;
import java.util.Locale;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.VectorSimilarityFunction;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.KnnFloatVectorQuery;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.apache.lucene.tests.util.TestUtil;

/**
 * An index written by Lucene 10.3.2 (and the same one written by 9.12.3) with
 * Lucene99HnswScalarQuantizedVectorsFormat (7 bits, default confidence interval) on an EUCLIDEAN
 * field "euc7", dimension 4, no merges:
 *
 * <ul>
 *   <li>segment _0: a0, a1, a2, all with the vector [1, 1, 1, 1] (so the segment's stored
 *       quantiles are min == max == 1);
 *   <li>segment _1: b0 = [2, 1, 1, 1], b1 = [3, 3, 3, 3], b2 = [-1, 0, 2, 5].
 * </ul>
 *
 * <p>The query [1, 1, 1, 1] is an exact match for a0..a2 (EUCLIDEAN similarity 1.0) and at
 * distance 1 from b0 (similarity 0.5). Searched with the release that wrote it, a0..a2 score 1.0
 * and rank first. The fixture is generated by GenUniformSqIndex (see the repro directory).
 */
public class TestOldUniformQuantizedSegment extends LuceneTestCase {

  /** zip of the index directory written by Lucene 10.3.2: GenUniformSqIndex OUT euc7 cfs */
  static final String INDEX_10_3_2 =
      "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";

  /** zip of the index directory written by Lucene 9.12.3: GenUniformSqIndex OUT euc7 cfs */
  static final String INDEX_9_12_3 =
      "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"
      + "XzJQSwUGAAAAAAcABwBuAQAAtQoAAAAA";

  public void testExactMatchInOldUniformSegment_10_3_2() throws Exception {
    checkExactMatchRanksFirst(INDEX_10_3_2);
  }

  public void testExactMatchInOldUniformSegment_9_12_3() throws Exception {
    checkExactMatchRanksFirst(INDEX_9_12_3);
  }

  private void checkExactMatchRanksFirst(String zipBase64) throws Exception {
    Path path = createTempDir("old-sq-index");
    TestUtil.unzip(new ByteArrayInputStream(Base64.getDecoder().decode(zipBase64)), path);
    float[] query = {1f, 1f, 1f, 1f};
    try (Directory dir = FSDirectory.open(path);
        DirectoryReader reader = DirectoryReader.open(dir)) {
      assertEquals(2, reader.leaves().size());
      IndexSearcher searcher = new IndexSearcher(reader);
      TopDocs all = searcher.search(new KnnFloatVectorQuery("euc7", query, 6), 6);
      StringBuilder hits = new StringBuilder();
      float bestA = Float.NaN, b0 = Float.NaN;
      for (ScoreDoc sd : all.scoreDocs) {
        String id = searcher.storedFields().document(sd.doc).get("id");
        hits.append(String.format(Locale.ROOT, " %s=%.9g", id, sd.score));
        if (id.startsWith("a") && Float.isNaN(bestA)) {
          bestA = sd.score;
        }
        if (id.equals("b0")) {
          b0 = sd.score;
        }
      }
      float exactA = VectorSimilarityFunction.EUCLIDEAN.compare(query, new float[] {1, 1, 1, 1});
      float exactB0 = VectorSimilarityFunction.EUCLIDEAN.compare(query, new float[] {2, 1, 1, 1});
      // the exact match must not score below a document at distance 1 (exact scores 1.0 vs 0.5)
      assertTrue(
          "exact-match documents of the old segment score "
              + bestA
              + " (exact similarity "
              + exactA
              + ") below b0 "
              + b0
              + " (exact similarity "
              + exactB0
              + "); hits:"
              + hits,
          bestA >= b0);
      TopDocs top1 = searcher.search(new KnnFloatVectorQuery("euc7", query, 1), 1);
      String topId = searcher.storedFields().document(top1.scoreDocs[0].doc).get("id");
      assertTrue("top-1 for an exact-match query is " + topId + "; hits:" + hits, topId.startsWith("a"));
    }
  }
}
