package cloud.nstart.terravault.ordinis.bench;

import cloud.nstart.terravault.ordinis.restapi.service.reference.OnCloseAction;
import cloud.nstart.terravault.ordinis.restapi.service.reference.ReferenceValidator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;

import java.util.concurrent.TimeUnit;

/**
 * Microbenchmarks для hot paths из dict-relationships-v2 epic.
 *
 * <p>Цели:
 * <ul>
 *   <li>{@link ReferenceValidator#parseRef} — schema reference parsing
 *       (вызов на bundle import + per-record validation).</li>
 *   <li>{@link OnCloseAction#fromString} — case-insensitive enum lookup
 *       (вызов в parseRef для каждой ref'и).</li>
 *   <li>JSONB schema traversal — типичный shape (40 properties в schema).</li>
 * </ul>
 *
 * <p>Run:
 * <pre>
 *   mvn -P bench -pl ordinis-bench -am package
 *   java -jar ordinis-bench/target/benchmarks.jar Lineage -prof gc
 * </pre>
 *
 * <p>Baseline numbers (M3 MacBook Pro, JDK 25, single thread):
 * <ul>
 *   <li>parseRef simple: ~150-200 ns/op</li>
 *   <li>parseRef + onClose: ~250-300 ns/op</li>
 *   <li>OnCloseAction.fromString: ~30-50 ns/op</li>
 *   <li>schema traversal (40 props, 5 with x-references): ~3-5 µs/op</li>
 * </ul>
 *
 * <p>Regression budget: 2x slowdown vs baseline → fails CI bench gate
 * (TODO: gating job, manual triggered now).
 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(value = 1, warmups = 1)
@State(Scope.Benchmark)
public class LineageBenchmark {

  private static final ObjectMapper OM = new ObjectMapper();

  /** Cached schema: 40 properties, 5 имеют x-references (+ 1 с x-references-on-close). */
  private JsonNode schema;
  /** Single property schema c x-references + x-references-on-close. */
  private JsonNode propWithOnClose;
  /** Single property schema только с x-references. */
  private JsonNode propBare;

  @Setup
  public void setup() throws Exception {
    StringBuilder sb = new StringBuilder("{\"type\":\"object\",\"properties\":{");
    for (int i = 0; i < 40; i++) {
      if (i > 0) sb.append(",");
      sb.append("\"field_").append(i).append("\":");
      if (i < 5) {
        // 5 properties — FK refs.
        sb.append("{\"type\":\"string\",\"x-references\":\"target_dict_")
            .append(i).append(".code\"");
        if (i == 0) sb.append(",\"x-references-on-close\":\"cascade\"");
        sb.append("}");
      } else {
        sb.append("{\"type\":\"string\"}");
      }
    }
    sb.append("}}");
    schema = OM.readTree(sb.toString());
    propWithOnClose = OM.readTree(
        "{\"type\":\"string\",\"x-references\":\"d.f\","
            + "\"x-references-on-close\":\"cascade\"}");
    propBare = OM.readTree("{\"type\":\"string\",\"x-references\":\"d.f\"}");
  }

  @Benchmark
  public ReferenceValidator.ParsedRef parseRef_simple() {
    return ReferenceValidator.parseRef("satellite_type.code");
  }

  @Benchmark
  public ReferenceValidator.ParsedRef parseRef_withOnClose() {
    return ReferenceValidator.parseRef("satellite_type.code", propWithOnClose);
  }

  @Benchmark
  public ReferenceValidator.ParsedRef parseRef_withSchemaButNoOnClose() {
    return ReferenceValidator.parseRef("satellite_type.code", propBare);
  }

  @Benchmark
  public OnCloseAction onCloseAction_block() {
    return OnCloseAction.fromString("block");
  }

  @Benchmark
  public OnCloseAction onCloseAction_cascade() {
    return OnCloseAction.fromString("cascade");
  }

  @Benchmark
  public OnCloseAction onCloseAction_null() {
    return OnCloseAction.fromString(null);
  }

  /**
   * Симулирует hot path в LineageIndexService.findSchemaDependents:
   * iterate properties, найти x-references markers, parse каждый.
   * 40 props × 5 refs = realistic шар для bundle с medium-sized dict.
   */
  @Benchmark
  public void schemaTraversal_findRefs(Blackhole bh) {
    JsonNode props = schema.get("properties");
    var it = props.fields();
    while (it.hasNext()) {
      var entry = it.next();
      JsonNode propSchema = entry.getValue();
      JsonNode refNode = propSchema.get("x-references");
      if (refNode == null || !refNode.isTextual()) continue;
      var parsed = ReferenceValidator.parseRef(refNode.asText(), propSchema);
      bh.consume(parsed);
    }
  }
}
