
package io.seata.rm.tcc.remoting.parser;

import io.seata.common.exception.FrameworkException;
import io.seata.common.util.ReflectionUtil;
import io.seata.rm.tcc.remoting.Protocols;
import io.seata.rm.tcc.remoting.RemotingDesc;

/**
 * HSF Remote Bean Parser
 *
 * @author ppf@jiumao.org
 */
public class HSFRemotingParser extends AbstractedRemotingParser {

    /**
     * is HSF env
     */
    private static volatile boolean isHsf;

    static {
        // check HSF runtime env
        try {
            Class.forName("com.taobao.hsf.app.api.util.HSFApiConsumerBean");
            Class.forName("com.taobao.hsf.app.api.util.HSFApiProviderBean");

            Class.forName("com.taobao.hsf.app.spring.util.HSFSpringConsumerBean");
            Class.forName("com.taobao.hsf.app.spring.util.HSFSpringProviderBean");

            isHsf = true;
        } catch (ClassNotFoundException e) {
            isHsf = false;
        }
    }

    @Override
    public boolean isRemoting(Object bean, String beanName) {
        return isHsf && (isReference(bean, beanName) || isService(bean, beanName));
    }

    @Override
    public boolean isReference(Object bean, String beanName) {
        String beanClassName = bean.getClass().getName();
        return isHsf && ("com.taobao.hsf.app.spring.util.HSFSpringConsumerBean".equals(beanClassName) || "org.springframework.beans.factory.FactoryBean".equals(beanClassName));
    }

    @Override
    public boolean isService(Object bean, String beanName) {
        String beanClassName = bean.getClass().getName();
        return isHsf && "com.taobao.hsf.app.spring.util.HSFSpringProviderBean".equals(beanClassName);
    }

    @Override
    public RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException {
        if (!this.isRemoting(bean, beanName)) {
            return null;
        }
        try {
            if (isReference(bean, beanName)) {
                Object consumerBean = ReflectionUtil.getFieldValue(bean, "consumerBean");
                Object metadata = ReflectionUtil.invokeMethod(consumerBean, "getMetadata");

                Class<?> interfaceClass = (Class<?>) ReflectionUtil.invokeMethod(metadata, "getIfClazz");
                String interfaceClassName = (String) ReflectionUtil.invokeMethod(metadata, "getInterfaceName");
                String uniqueId = (String) ReflectionUtil.invokeMethod(metadata, "getVersion");
                String group = (String) ReflectionUtil.invokeMethod(metadata, "getGroup");

                RemotingDesc serviceBeanDesc = new RemotingDesc();
                serviceBeanDesc.setServiceClass(interfaceClass);
                serviceBeanDesc.setServiceClassName(interfaceClassName);
                serviceBeanDesc.setUniqueId(uniqueId);
                serviceBeanDesc.setGroup(group);
                serviceBeanDesc.setProtocol(Protocols.HSF);
                return serviceBeanDesc;
            } else if (isService(bean, beanName)) {
                Object consumerBean = ReflectionUtil.getFieldValue(bean, "providerBean");
                Object metadata = ReflectionUtil.invokeMethod(consumerBean, "getMetadata");

                String interfaceClassName = (String) ReflectionUtil.invokeMethod(metadata, "getInterfaceName");
                Class<?> interfaceClass = (Class<?>) Class.forName(interfaceClassName);
                String uniqueId = (String) ReflectionUtil.invokeMethod(metadata, "getVersion");
                String group = (String) ReflectionUtil.invokeMethod(metadata, "getGroup");
                RemotingDesc serviceBeanDesc = new RemotingDesc();
                serviceBeanDesc.setServiceClass(interfaceClass);
                serviceBeanDesc.setServiceClassName(interfaceClassName);
                serviceBeanDesc.setUniqueId(uniqueId);
                serviceBeanDesc.setGroup(group);

                Object targetBean = ReflectionUtil.getFieldValue(metadata, "target");
                serviceBeanDesc.setTargetBean(targetBean);
                serviceBeanDesc.setProtocol(Protocols.HSF);
                return serviceBeanDesc;
            }
        } catch (Throwable t) {
            throw new FrameworkException(t);
        }
        return null;
    }


    @Override
    public short getProtocol() {
        return Protocols.HSF;
    }
}
