package com.eshore.gringotts.web.domain.service;

import com.eshore.gringotts.web.domain.base.service.SimpleServiceSupport;
import com.eshore.gringotts.web.domain.entity.Authentication;
import com.eshore.gringotts.web.domain.entity.Authentication_;
import com.eshore.gringotts.web.domain.entity.Confirmation;
import com.eshore.gringotts.web.domain.entity.Confirmation_;
import com.eshore.gringotts.web.domain.entity.DataResource;
import com.eshore.gringotts.web.domain.entity.DataResource_;
import com.eshore.gringotts.web.domain.entity.User;
import com.eshore.gringotts.web.domain.entity.Ware;
import com.eshore.gringotts.web.domain.entity.format.ResourceFormat;
import com.eshore.gringotts.web.domain.entity.type.ResourceType;
import com.eshore.gringotts.web.domain.repository.DataResourceRepository;
import com.eshore.gringotts.web.domain.repository.ResourceFormatRepository;
import com.eshore.gringotts.web.domain.repository.ResourceTypeRepository;
import com.eshore.gringotts.web.helper.EntityHelper;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.list.ImmutableList;
import org.springframework.stereotype.Service;

/**
 * 数据资源服务
 *
 * @author lanyuanxiaoyao
 */
@Slf4j
@Service
public class DataResourceService extends SimpleServiceSupport<DataResource> {
    private final DataResourceRepository dataResourceRepository;
    private final ResourceTypeRepository resourceTypeRepository;
    private final ResourceFormatRepository resourceFormatRepository;
    private final UserService userService;

    public DataResourceService(DataResourceRepository repository, ResourceTypeRepository resourceTypeRepository, ResourceFormatRepository resourceFormatRepository, UserService userService) {
        super(repository, userService);
        this.dataResourceRepository = repository;
        this.resourceTypeRepository = resourceTypeRepository;
        this.resourceFormatRepository = resourceFormatRepository;
        this.userService = userService;
    }

    @Override
    protected ImmutableList<Predicate> listPredicate(Root<DataResource> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        User loginUser = userService.currentLoginUser();
        if (User.isAdministrator(loginUser)) {
            return Lists.immutable.empty();
        }

        Subquery<Confirmation> confirmationSubquery = query.subquery(Confirmation.class);
        Root<Confirmation> confirmationRoot = confirmationSubquery.from(Confirmation.class);
        confirmationSubquery.select(confirmationRoot)
                .where(
                        builder.equal(confirmationRoot.get(Confirmation_.target), root),
                        EntityHelper.checkNeededEntityPrediction(confirmationRoot, builder, loginUser)
                );

        Subquery<Authentication> authenticationSubquery = query.subquery(Authentication.class);
        Root<Authentication> authenticationRoot = authenticationSubquery.from(Authentication.class);
        authenticationSubquery.select(authenticationRoot)
                .where(
                        builder.equal(authenticationRoot.get(Authentication_.target), root),
                        EntityHelper.checkNeededEntityPrediction(authenticationRoot, builder, loginUser)
                );

        return Lists.immutable.of(builder.or(
                builder.equal(root.get(DataResource_.createdUser), loginUser),
                builder.exists(confirmationSubquery),
                builder.exists(authenticationSubquery)
        ));
    }

    public ImmutableList<DataResource> listNoConfirmation() {
        return Lists.immutable.ofAll(dataResourceRepository.findAll(
                (root, query, builder) -> {
                    Join<DataResource, Confirmation> confirmationJoin = root.join(DataResource_.confirmation, JoinType.LEFT);
                    return builder.and(
                            builder.isNull(confirmationJoin.get(Confirmation_.id)),
                            builder.equal(root.get(DataResource_.createdUser), userService.currentLoginUser())
                    );
                }
        ));
    }

    public ImmutableList<DataResource> listNoAuthentication() {
        return Lists.immutable.ofAll(dataResourceRepository.findAll(
                (root, query, builder) -> {
                    Join<DataResource, Confirmation> confirmationJoin = root.join(DataResource_.confirmation, JoinType.LEFT);
                    confirmationJoin.on(builder.equal(confirmationJoin.get(Confirmation_.state), Confirmation.State.NORMAL));

                    Subquery<Authentication> authenticationSubquery = query.subquery(Authentication.class);
                    Root<Authentication> authenticationRoot = authenticationSubquery.from(Authentication.class);
                    authenticationSubquery.select(authenticationRoot)
                            .where(
                                    builder.equal(authenticationRoot.get(Authentication_.target), root),
                                    builder.equal(authenticationRoot.get(Authentication_.createdUser), userService.currentLoginUser())
                            );
                    return builder.and(
                            builder.exists(authenticationSubquery).not(),
                            builder.equal(root.get(DataResource_.createdUser), userService.currentLoginUser())
                    );
                }
        ));
    }

    public ImmutableList<DataResource> listNoWare() {
        return Lists.immutable.ofAll(dataResourceRepository.findAll(
                (root, query, builder) -> {
                    Join<DataResource, Confirmation> confirmationJoin = root.join(DataResource_.confirmation, JoinType.LEFT);
                    Join<DataResource, Ware> wareJoin = root.join(DataResource_.ware, JoinType.LEFT);
                    return builder.and(
                            builder.isNotNull(confirmationJoin),
                            builder.equal(confirmationJoin.get(Confirmation_.state), Confirmation.State.NORMAL),
                            builder.isNull(wareJoin),
                            builder.equal(root.get(DataResource_.createdUser), userService.currentLoginUser())
                    );
                }
        ));
    }

    @Override
    public Long save(DataResource entity) {
        ResourceType type = resourceTypeRepository.save(entity.getType());
        ResourceFormat format = resourceFormatRepository.save(entity.getFormat());
        entity.setType(type);
        entity.setFormat(format);
        return super.save(entity);
    }
}
