package cloud.nstart.terravault.ordinis.domain.record;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;

/**
 * MV-backed реализация {@link DependentsQuery}. Активируется через property
 * {@code ordinis.lineage.mv.enabled=true} (default false). Когда выключена —
 * Spring инжектит {@code DependentsQueryJdbc} (без MV, прямой JSONB query).
 *
 * <p>Производительность: один SQL по composite index
 * {@code (target_dict_name, target_business_key_value)} вместо N JSONB
 * lookups (по одному на каждый source dict).
 *
 * <p>Stale window: MV refresh throttled на 1/min (см. {@code MaterializedViewRefreshService}).
 * Записи закрытые между refresh'ами всё равно отфильтрованы query-side через
 * {@code valid_from/valid_to} columns в MV — actual valid_to update сделан
 * через {@code DictionaryRecord} table refresh, MV отстаёт только по новым
 * referencing records которые ещё не подхвачены MV.
 *
 * <p>Active filter в WHERE clause использует {@code valid_from <= ? AND valid_to > ?}
 * (тот же at) что и в JdbcImpl — consistent semantics.
 */
@Component
@Primary
@ConditionalOnProperty(name = "ordinis.lineage.mv.enabled", havingValue = "true", matchIfMissing = false)
public class DependentsQueryMv implements DependentsQuery {

  private static final Logger log = LoggerFactory.getLogger(DependentsQueryMv.class);

  private final JdbcTemplate jdbc;

  @Autowired
  public DependentsQueryMv(JdbcTemplate jdbc) {
    this.jdbc = jdbc;
    log.info("DependentsQuery: MV-backed implementation активирована "
        + "(ordinis.lineage.mv.enabled=true). Query path использует "
        + "record_dependents_index materialized view.");
  }

  @Override
  public List<DependentRow> findActive(
      UUID sourceDictionaryId,
      String sourceField,
      String targetValue,
      OffsetDateTime at,
      int limit,
      int offset) {
    if (limit <= 0 || limit > 500) {
      throw new IllegalArgumentException("limit must be 1..500, got " + limit);
    }
    if (offset < 0) {
      throw new IllegalArgumentException("offset must be >= 0, got " + offset);
    }

    // MV path: source_dict_id + source_field + target_value уникально определяет ряды.
    String sql = """
        SELECT source_record_id, source_dict_id, source_business_key,
               valid_from, valid_to, created_at
        FROM record_dependents_index
        WHERE source_dict_id = ?
          AND source_field = ?
          AND target_business_key_value = ?
          AND valid_from <= ?
          AND valid_to > ?
        ORDER BY created_at DESC
        LIMIT ? OFFSET ?
        """;

    return jdbc.query(
        sql,
        (rs, n) -> new DependentRow(
            (UUID) rs.getObject("source_record_id"),
            (UUID) rs.getObject("source_dict_id"),
            rs.getString("source_business_key"),
            rs.getObject("valid_from", OffsetDateTime.class),
            rs.getObject("valid_to", OffsetDateTime.class),
            rs.getObject("created_at", OffsetDateTime.class)),
        sourceDictionaryId, sourceField, targetValue, at, at, limit, offset);
  }

  @Override
  public long countActive(
      UUID sourceDictionaryId,
      String sourceField,
      String targetValue,
      OffsetDateTime at) {
    String sql = """
        SELECT COUNT(*) FROM record_dependents_index
        WHERE source_dict_id = ?
          AND source_field = ?
          AND target_business_key_value = ?
          AND valid_from <= ?
          AND valid_to > ?
        """;

    Long count = jdbc.queryForObject(sql, Long.class,
        sourceDictionaryId, sourceField, targetValue, at, at);
    return count == null ? 0 : count;
  }
}
