package com.magistr.app.service;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Component;

import java.time.LocalDate;

@Component
public class PostgreSqlAdvisoryLock {

    private static final int SCHEDULE_OVERRIDE_DATE_NAMESPACE = 0x4D414744;
    private static final long SCHEDULE_OVERRIDE_ID_NAMESPACE_MASK = Long.MIN_VALUE;

    @PersistenceContext
    private EntityManager entityManager;

    public void lockScheduleOverrideDate(LocalDate date) {
        if (date == null) {
            throw new IllegalArgumentException("Дата блокировки расписания обязательна");
        }
        int exactEpochDay;
        try {
            exactEpochDay = Math.toIntExact(date.toEpochDay());
        } catch (ArithmeticException exception) {
            throw new IllegalArgumentException("Дата находится за пределами диапазона PostgreSQL", exception);
        }
        entityManager.createNativeQuery("select pg_advisory_xact_lock(?1, ?2)")
                .setParameter(1, SCHEDULE_OVERRIDE_DATE_NAMESPACE)
                .setParameter(2, exactEpochDay)
                .getSingleResult();
    }

    public void lockScheduleOverrideId(Long overrideId) {
        if (overrideId == null || overrideId <= 0) {
            throw new IllegalArgumentException("Идентификатор точечного изменения должен быть положительным");
        }
        // Одноаргументное пространство advisory-lock не пересекается с двухаргументным пространством дат.
        long lockKey = overrideId ^ SCHEDULE_OVERRIDE_ID_NAMESPACE_MASK;
        entityManager.createNativeQuery("select pg_advisory_xact_lock(?1)")
                .setParameter(1, lockKey)
                .getSingleResult();
    }
}
