package cloud.nstart.terravault.ordinis.restapi.web;

import cloud.nstart.terravault.ordinis.auth.ScopeContext;
import cloud.nstart.terravault.ordinis.domain.DataScope;
import cloud.nstart.terravault.ordinis.domain.record.RecordSearchQuery;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Smart search across all dictionaries (CEO plan v1 stretch).
 *
 * <p>{@code GET /api/v1/search?q=…&size=…&perDict=…} — free-form ILIKE
 * по {@code dictionary_records.data::text} с trigram индексом. Возвращает
 * grouped results per dict {@link RecordSearchQuery.SearchHit}.
 *
 * <p>Min query length = 3 (чтобы использовать trigram index). Backend
 * silently возвращает empty list для shorter queries.
 *
 * <p>Scope filter: только dicts которых caller видит через scope.
 *
 * <p>Phase v1 limits: max 10 per dict, max 100 total. Достаточно для admin
 * "find this code" use case. Для browser-style search-as-you-type — нужен
 * dedicated index server (Elastic / Meili) — отложен в v2.
 */
@RestController
@RequestMapping("/api/v1/search")
public class SearchController {

  private final RecordSearchQuery searchQuery;
  private final ScopeContext scopeContext;

  public SearchController(RecordSearchQuery searchQuery, ScopeContext scopeContext) {
    this.searchQuery = searchQuery;
    this.scopeContext = scopeContext;
  }

  /**
   * @param q         free-form text (min 3 chars; <3 returns empty).
   * @param size      total cap (default 100, max 500).
   * @param perDict   per-dictionary cap (default 10, max 100).
   * @return grouped по dict_name с display name + count + items.
   */
  @GetMapping
  public SearchResponse search(
      @RequestParam String q,
      @RequestParam(defaultValue = "100") int size,
      @RequestParam(defaultValue = "10") int perDict) {
    var scopeStrings = scopeContext.currentScopes().stream()
        .map(DataScope::name)
        // DB column data_scope использует lowercase per CHECK constraint
        // ("public"/"internal"/"restricted"). Mapping enum → lowercase.
        .map(String::toLowerCase)
        .collect(Collectors.toList());

    List<RecordSearchQuery.SearchHit> hits = searchQuery.search(
        q, scopeStrings, OffsetDateTime.now(), perDict, size);

    // Group by dict for clean UI rendering.
    Map<String, List<RecordSearchQuery.SearchHit>> grouped = hits.stream()
        .collect(Collectors.groupingBy(
            RecordSearchQuery.SearchHit::dictName,
            java.util.LinkedHashMap::new,
            Collectors.toList()));

    List<DictGroup> groups = grouped.entrySet().stream()
        .map(e -> {
          var first = e.getValue().get(0);
          return new DictGroup(
              e.getKey(),
              first.dictDisplayName(),
              e.getValue().size(),
              e.getValue().stream()
                  .map(h -> new SearchItem(
                      h.businessKey(), h.dataScope(), h.createdAt()))
                  .toList());
        })
        .toList();

    return new SearchResponse(q, hits.size(), groups);
  }

  public record SearchItem(String businessKey, String dataScope, OffsetDateTime createdAt) {}

  public record DictGroup(
      String dictName,
      String dictDisplayName,
      int count,
      List<SearchItem> items) {}

  public record SearchResponse(String query, int total, List<DictGroup> groups) {}
}
