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.DataFile;
import com.eshore.gringotts.web.domain.entity.DataFile_;
import com.eshore.gringotts.web.domain.entity.User;
import com.eshore.gringotts.web.domain.repository.DataFileRepository;
import com.eshore.gringotts.web.helper.EntityHelper;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
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
 * @date 2024-11-21
 */
@Slf4j
@Service
public class DataFileService extends SimpleServiceSupport<DataFile> {
    private final DataFileRepository dataFileRepository;
    private final UserService userService;

    public DataFileService(DataFileRepository dataFileRepository, UserService userService) {
        super(dataFileRepository, userService);
        this.dataFileRepository = dataFileRepository;
        this.userService = userService;
    }

    @Override
    protected ImmutableList<Predicate> listPredicate(Root<DataFile> 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.isMember(root, confirmationRoot.get(Confirmation_.evidences)),
                        EntityHelper.checkNeededEntityPrediction(confirmationRoot, builder, loginUser)
                );

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

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

    public DataFile downloadFile(Long id) {
        return dataFileRepository.findOne((root, query, builder) -> builder.equal(root.get(DataFile_.id), id)).orElseThrow(DataFileNotFoundException::new);
    }

    public Long initialDataFile(String filename) {
        DataFile dataFile = new DataFile();
        dataFile.setFilename(filename);
        return dataFileRepository.save(dataFile).getId();
    }

    public void updateDataFile(Long id, String path, Long size, String md5, String type) {
        DataFile dataFile = dataFileRepository.findById(id).orElseThrow(UpdateDataFileFailedException::new);
        dataFile.setSize(size);
        dataFile.setMd5(md5);
        dataFile.setPath(path);
        dataFile.setType(type);
        User loginUser = userService.currentLoginUser();
        dataFile.setModifiedUser(loginUser);
        dataFileRepository.save(dataFile);
    }

    public static final class DataFileNotFoundException extends RuntimeException {
        public DataFileNotFoundException() {
            super("文件未找到，请重新上传");
        }
    }

    public static final class UpdateDataFileFailedException extends RuntimeException {
        public UpdateDataFileFailedException() {
            super("更新文件信息失败，请重新上传");
        }
    }
}
