package cn.lihongjie.coal.passwordDict.service;

import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.common.Constants;
import cn.lihongjie.coal.passwordDict.dto.CreatePasswordDictDto;
import cn.lihongjie.coal.passwordDict.dto.PasswordDictDto;
import cn.lihongjie.coal.passwordDict.dto.UpdatePasswordDictDto;
import cn.lihongjie.coal.passwordDict.entity.PasswordDictEntity;
import cn.lihongjie.coal.passwordDict.mapper.PasswordDictMapper;
import cn.lihongjie.coal.passwordDict.repository.PasswordDictRepository;
import cn.lihongjie.coal.sysconfig.service.SysConfigService;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Service
@Slf4j
@Transactional
public class PasswordDictService extends BaseService<PasswordDictEntity, PasswordDictRepository> {
    @Value("classpath:passwordDict/dict.zip")
    Resource dictFile;
    @Autowired SysConfigService sysConfigService;
    @Autowired private PasswordDictRepository repository;
    @Autowired private PasswordDictMapper mapper;
    @Autowired private ConversionService conversionService;
    @PersistenceContext
    private EntityManager em;

    public PasswordDictDto create(CreatePasswordDictDto request) {
        PasswordDictEntity entity = mapper.toEntity(request);

        this.repository.save(entity);
        return getById(entity.getId());
    }

    public PasswordDictDto update(UpdatePasswordDictDto request) {
        PasswordDictEntity entity = this.repository.get(request.getId());
        this.mapper.updateEntity(entity, request);

        this.repository.save(entity);

        return getById(entity.getId());
    }

    public void delete(IdRequest request) {
        this.repository.deleteAllById(request.getIds());
    }

    public PasswordDictDto getById(String id) {
        PasswordDictEntity entity = repository.get(id);

        return mapper.toDto(entity);
    }

    public Page<PasswordDictDto> list(CommonQuery query) {
        Page<PasswordDictEntity> page =
                repository.findAll(
                        query.specification(conversionService),
                        PageRequest.of(
                                query.getPageNo(),
                                query.getPageSize(),
                                Sort.by(query.getOrders())));

        return page.map(this.mapper::toDto);
    }

    public boolean isInDict(String password) {

        return this.repository.countByPass(password) > 0;
    }

    @SneakyThrows
    public void initDict(){

        if (sysConfigService.isEnable(Constants.SYSCONFIG_PASSWORD_DICT_DETECT)) {
            if (this.repository.count() == 0){

                log.info("开始初始化密码字典");
                AtomicInteger count = new AtomicInteger();

                try (var i = new ZipInputStream(dictFile.getInputStream())){

                    ZipEntry entry = i.getNextEntry();

                    while (entry != null){

                        BufferedReader reader = new BufferedReader(new InputStreamReader(i));

                        reader.lines().forEach(x -> {
                            PasswordDictEntity dict = new PasswordDictEntity();
                            dict.setPass(x);


                            this.repository.save(dict);

                            int increment = count.getAndIncrement();

                            if (increment % 10000 == 0){
                                log.info("已经初始化 {} 条密码", increment);
                                em.flush();
                            }


                        });

                        entry = i.getNextEntry();
                    }

                    em.flush();

                }

            }
        }
    }
}
