package cloud.nstart.terravault.ordinis.outbox;

import cloud.nstart.terravault.ordinis.domain.DataScope;
import cloud.nstart.terravault.ordinis.domain.outbox.OutboxEvent;
import cloud.nstart.terravault.ordinis.domain.outbox.OutboxEventRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.tracing.Tracer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * Запись события в outbox в той же транзакции что и изменение справочной
 * записи. Гарантирует at-least-once публикацию (poller потом отправит в Kafka).
 *
 * <p>Tracer optional — если в кластере включён OTel, prefix trace_id/span_id
 * прокинется в outbox row для корреляции.
 */
@Component
public class OutboxRecorder {

  private final OutboxEventRepository repository;
  private final ObjectMapper objectMapper;
  private final ObjectProvider<Tracer> tracerProvider;

  public OutboxRecorder(
      OutboxEventRepository repository,
      ObjectMapper objectMapper,
      ObjectProvider<Tracer> tracerProvider) {
    this.repository = repository;
    this.objectMapper = objectMapper;
    this.tracerProvider = tracerProvider;
  }

  /**
   * @param eventType строковый тип (например "RecordCreated")
   * @param aggregateType "DictionaryRecord" или "DictionaryDefinition"
   * @param aggregateId стабильный id аггрегата (UUID или dictionary name)
   * @param dictionaryName имя словаря (для рутинга в Kafka топик и для логов)
   * @param scope scope записи → определяет в какой топик
   * @param bundle bundle (cuod, energy, ...)
   * @param kafkaKey ключ публикации (обычно {@code <dictionaryName>:<businessKey>})
   * @param payload объект-payload (будет сериализован в JsonNode)
   */
  @Transactional(propagation = org.springframework.transaction.annotation.Propagation.MANDATORY)
  public OutboxEvent record(
      String eventType,
      String aggregateType,
      String aggregateId,
      String dictionaryName,
      DataScope scope,
      String bundle,
      String kafkaKey,
      Object payload) {

    JsonNode payloadJson = objectMapper.valueToTree(payload);
    String topic = KafkaTopicResolver.topic(bundle, scope);

    OutboxEvent event = new OutboxEvent(
        eventType, aggregateType, aggregateId, scope, payloadJson, topic, kafkaKey);
    event.setDictionaryName(dictionaryName);

    Tracer tracer = tracerProvider.getIfAvailable();
    if (tracer != null) {
      var span = tracer.currentSpan();
      if (span != null) {
        event.setTraceId(span.context().traceId());
        event.setSpanId(span.context().spanId());
      }
    }

    return repository.save(event);
  }
}
