/* (C) 2024 YiRing, Inc. */
package com.yiring.common.snowflake;

import static org.hibernate.generator.EventTypeSets.INSERT_ONLY;
import static org.hibernate.internal.util.ReflectHelper.getPropertyType;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import java.io.Serial;
import java.lang.reflect.Member;
import java.util.Arrays;
import java.util.EnumSet;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.EventType;
import org.hibernate.generator.EventTypeSets;
import org.hibernate.generator.GeneratorCreationContext;
import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext;

/**
 * @author Jim
 */
@SuppressWarnings({ "unused" })
public class SnowflakeIdGeneratorAccomplish implements BeforeExecutionGenerator {

    @Serial
    private static final long serialVersionUID = 245241369672012904L;

    private final Snowflake snowflake;
    private final Class<?> type;

    private SnowflakeIdGeneratorAccomplish(SnowflakeIdGenerator config, Member idMember) {
        final Class<?> propertyType = getPropertyType(idMember);
        if (!Arrays.asList(String.class, Long.class).contains(propertyType)) {
            throw new HibernateException(
                "Unanticipated return type [" + propertyType.getName() + "] for SnowflakeId conversion"
            );
        }

        snowflake = IdUtil.getSnowflake();
        type = propertyType;
    }

    public SnowflakeIdGeneratorAccomplish(
        SnowflakeIdGenerator config,
        Member idMember,
        CustomIdGeneratorCreationContext creationContext
    ) {
        this(config, idMember);
    }

    public SnowflakeIdGeneratorAccomplish(
        SnowflakeIdGenerator config,
        Member member,
        GeneratorCreationContext creationContext
    ) {
        this(config, member);
    }

    /**
     * @return {@link EventTypeSets#INSERT_ONLY}
     */
    @Override
    public EnumSet<EventType> getEventTypes() {
        return INSERT_ONLY;
    }

    @Override
    public Object generate(
        SharedSessionContractImplementor session,
        Object owner,
        Object currentValue,
        EventType eventType
    ) {
        if (type == String.class) {
            return snowflake.nextIdStr();
        } else {
            return snowflake.nextId();
        }
    }
}
