/*
 * Copyright (c) 京北方信息技术股份有限公司 Corporation 2021 . All rights reserved.
 *
 */
package net.northking.cctp.common.jpa;

import net.northking.cctp.common.db.DefaultPagination;
import net.northking.cctp.common.db.Pagination;
import net.northking.cctp.common.db.entity.Entity;
import net.northking.cctp.common.http.QueryByPage;
import net.northking.cctp.common.saas.db.TenantPartition;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.util.Date;
import java.util.Optional;

/**
 * CCTP平台的JPA Repository
 *
 * @author : xiusong.xie
 * @version : 1.0
 */
public class CctpJpaRepository<T, ID> extends SimpleJpaRepository<T, ID> implements BasicJpaRepository<T, ID>
{
  public CctpJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager)
  {
    super(entityInformation, entityManager);
  }

  public CctpJpaRepository(Class<T> domainClass, EntityManager em)
  {
    super(domainClass, em);
  }

  @Override
  public <S extends T> S save(S entity)
  {
    initEntity(entity);
    initTenant(entity);
    return super.save(entity);
  }

  /**
   * 设置 创建人、创建时间、修改人、修改时间<br>
   * 每次保存时都会设置这个四个字段，因此必须把实体类的 创建人、创建时间这两个字段设置成不可更新
   *
   * @param entity 数据库实体类
   * @param <S>    泛型数据库实体类
   */
  private <S extends T> void   initEntity(S entity)
  {
    if (entity instanceof Entity)
    {
      if (((Entity) entity).getCreatedTime() == null) {
        ((Entity) entity).setCreatedTime(new Date());
      }
      if (((Entity) entity).getCreatedBy() == null) {
        ((Entity) entity).setCreatedBy(NKSecurityContext.getUserId());
      }
      ((Entity) entity).setUpdatedTime(new Date());
      ((Entity) entity).setUpdatedBy(NKSecurityContext.getUserId());
    }
  }

  private <S extends T> void initTenant(S entity)
  {
    if (entity instanceof TenantPartition)
    {
      ((TenantPartition) entity).setTenantId(NKSecurityContext.getTenantId());
      ((TenantPartition) entity).setSchemaSuffix(NKSecurityContext.getDomains());
    }
  }

  @Transactional
  public <S extends T> Pagination<T> findAll(QueryByPage<S> queryByPage)
  {
    Pagination<T> pagination = new DefaultPagination<>(queryByPage.getPageNo(), queryByPage.getPageSize());
    S condition = queryByPage.getQuery();
    Page<T> page;
    if (condition == null)
    {// 无查询条件
      page = findAll(queryByPage.pageable());
    } else
    {// 有查询条件
      initTenant(condition);
      page = findAll(Example.of(condition), queryByPage.pageable());
    }
    pagination.setRecordCount(page.getTotalElements());
    pagination.setRecords(page.getContent());
    return pagination;
  }

  @Override
  public boolean isOwn(T record)
  {
    if (record instanceof TenantPartition)
    {
      return ((TenantPartition) record).getTenantId().equals(NKSecurityContext.getTenantId());
    } else
    {
      String err = record.getClass() + " is not extends TenantPartition";
      throw new ClassCastException(err);
    }
  }

  @Override
  @Transactional
  public Optional<T> findByIdAndTenant(ID id)
  {
    Optional<T> optional = findById(id);
    if (optional.isPresent() && isOwn(optional.get()))
    {
      return optional;
    } else
    {
      return Optional.empty();
    }
  }

  @Override
  @Transactional
  public int deleteByIdAndTenant(ID id)
  {
    Optional<T> optional = findByIdAndTenant(id);
    if (optional.isPresent())
    {
      delete(optional.get());
      return 1;
    }
    return 0;
  }

  @Override
  @Transactional
  public int deleteAllByIdsAndTenant(Iterable<ID> ids)
  {
    int count = 0;
    for (ID id : ids)
    {
      count += deleteByIdAndTenant(id);
    }
    return count;
  }
}
