package cn.reghao.tnb.user.app.config;

import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.concurrent.Callable;

/**
 * @author reghao
 * @date 2025-07-22 14:06:34
 */
@Slf4j
public class MyHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        /**
         * 1 获取当前线程的threadlocalmap
         */
        Object currentThreadlocalMap = getCurrentThreadlocalMap();

        Callable<T> finalCallable = new Callable<T>() {
            // 2
            private Object callerThreadlocalMap = currentThreadlocalMap;
            // 3
            private Callable<T> targetCallable = callable;

            @Override
            public T call() throws Exception {
                /**
                 * 4 将工作线程的原有线程变量保存起来
                 */
                Object oldThreadlocalMapOfWorkThread = getCurrentThreadlocalMap();
                /**
                 *5 将本线程的线程变量，设置为caller的线程变量
                 */
                setCurrentThreadlocalMap(callerThreadlocalMap);

                try {
                    // 6
                    return targetCallable.call();
                } finally {
                    // 7
                    setCurrentThreadlocalMap(oldThreadlocalMapOfWorkThread);
                    log.info("restore work thread's threadlocal");
                }

            }
        };

        return finalCallable;
    }

    private Object getCurrentThreadlocalMap() {
        Thread thread = Thread.currentThread();
        try {
            Field field = Thread.class.getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object o = field.get(thread);
            return o;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            log.error("{}", e);
        }
        return null;
    }

    private void setCurrentThreadlocalMap(Object newThreadLocalMap) {
        Thread thread = Thread.currentThread();
        try {
            Field field = Thread.class.getDeclaredField("threadLocals");
            field.setAccessible(true);
            field.set(thread,newThreadLocalMap);

        } catch (NoSuchFieldException | IllegalAccessException e) {
            log.error("{}", e);
        }
    }
}
