package cn.quant.baa.pay.context;

import cn.quant.baa.pay.component.Sequencer;
import cn.quant.baa.pay.jpa.entity.AccountEntity;
import cn.quant.baa.pay.model.BusinessRequest;
import cn.quant.spring.NullException;
import cn.quant.spring.context.BusinessSession;
import cn.quant.spring.context.ServerApplicationContext;
import cn.quant.spring.data.jpa.entity.OptimisticEntity;
import cn.quant.spring.data.jpa.repository.JapRepositoryProxy;
import cn.quant.spring.data.jpa.repository.RepositoryProxy;
import cn.quant.spring.util.DateUtils;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Date;

/**
 * <p>session 对象</p>
 * Created by Administrator on 2021/8/23 0023.
 */
public class TransactionSession extends BusinessSession {

    private final static ThreadLocal<TransactionSession> local = new ThreadLocal<>();

    private RepositoryProxy repositoryProxy;

    private AccountEntity account;

    private Sequencer sequencer;

    public TransactionSession(ServerApplicationContext context, Sequencer sequencer) {
        super(context);
        this.repositoryProxy = new JapRepositoryProxy(context.getParent());
        this.sequencer = sequencer;
    }

    public long nextId() {
        return sequencer.nextId();
    }

    public long nextId(long id) {
        return sequencer.nextId(id);
    }

    public int partitionKey(long id) {
        return sequencer.partitionKey(id);
    }

    /**
     * 获取session
     *
     * @return
     * @see cn.quant.baa.pay.annotation.BusinessMapping
     * @see cn.quant.baa.pay.aspect.BusinessMappingAspect
     */
    public static TransactionSession session() {
        return local.get();
    }

    public static void release() {
        local.remove();
    }

    public static TransactionSession newInstance(BusinessRequest request, ServerApplicationContext context, Sequencer sequencer) {
        TransactionSession session = local.get();
        if (session == null) {
            session = new TransactionSession(context, sequencer);
            if (request == null) {
                session.setBusinessDate(LocalDateTime.now());
                session.setRequestTime(DateUtils.now());
            } else {
                session.setRequestTime(request.getRequestTime());
                Date businessDate = request.getBusinessDate();
                if (businessDate == null) {
                    session.setBusinessDate(LocalDateTime.now());
                } else {
                    session.setBusinessDate(DateUtils.toLocalDateTime(businessDate));
                }
            }

            local.set(session);
        }
        return session;
    }

    public AccountEntity getAccount() {
        return account;
    }

    public void setAccount(AccountEntity account) {
        this.account = account;
    }

    //repository
    public RepositoryProxy getRepositoryProxy() {
        return repositoryProxy;
    }

    public <T> Collection<T> getEntity(Class<T> cls) {
        return (Collection<T>) repositoryProxy.get(cls).values();
    }

    public <T> T getEntity(Class<T> cls, Object id) {
        return (T) repositoryProxy.get(cls, id);
    }

    public <T extends OptimisticEntity> void pushEntity(T entity) {
        repositoryProxy.push(entity);
    }

    public <T extends OptimisticEntity> void pushEntity(Collection<T> entities) {
        repositoryProxy.push(entities);
    }

    public <T extends OptimisticEntity> void pushEntity(T... entities) {
        repositoryProxy.push(entities);
    }

    public void commit() {
        repositoryProxy.commit();
    }

    public void clearEntity() {
        repositoryProxy.clear();
    }

    public JpaRepository getRepository(Class cls) {
        JpaRepository repository = repositoryProxy.getRepository(cls);
        if (repository == null) {
            throw new NullException("Repository not found : {}", cls);
        }
        return repository;
    }

    public <T> T findOne(Class<T> cls, Object key) {
        RepositoryProxy repositoryProxy = this.getRepositoryProxy();
        T t = null;
        if (repositoryProxy != null) {
            t = (T) repositoryProxy.get(cls, key);
        }
        if (t == null) {
            return (T) repositoryProxy.getRepository(cls).findById(key).orElse(null);
        }
        return t;
    }
}
