package rd.service.major;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.yitter.idgen.YitIdHelper;
import rd.dao.mapper.major.MajorMapper;
import rd.data.code.ServiceCode;
import rd.data.db.table.Major;

import org.springframework.stereotype.Service;
import rd.exception.ServiceException;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class MajorService {
    private static final Cache<Long, Major> majorCache = Caffeine.newBuilder()
            .expireAfterAccess(30, TimeUnit.DAYS)
            .initialCapacity(16)
            .maximumSize(64)
            .build();

    private static final Cache<String, Major> majorNameCache = Caffeine.newBuilder()
            .expireAfterAccess(30, TimeUnit.DAYS)
            .initialCapacity(16)
            .maximumSize(64)
            .build();

    private final MajorMapper majorMapper;

    public MajorService(MajorMapper majorMapper) {
        this.majorMapper = majorMapper;
    }

    public Major addMajorIfNotExists(String name) {
        Major major = this.getMajorByName(name);
        if (major == null) {
            major = new Major();
            Long id = YitIdHelper.nextId();
            major.setId(id);
            major.setName(name);

            majorMapper.insert(major);

            major = getMajor(id, false);

            putCache(major);
        }

        return major;
    }

    public void deleteMajor(Long id) {
        var major = majorMapper.selectOne(new QueryWrapper<Major>()
                .select("name")
                .eq("id", id)
                .eq("deleted", false)
                .last("limit 1")
        );

        if (major == null) {
            return;
        }

        major.setDeleted(true);
        major.setEditTime(LocalDateTime.now());

        majorMapper.update(major, new QueryWrapper<Major>()
                .eq("id", id)
                .eq("deleted", false)
        );

        majorCache.invalidate(id);
        majorNameCache.invalidate(major.getName());
    }

    public Major getMajor(Long id, boolean useCache) {
        var major = majorCache.getIfPresent(id);
        if (useCache && major != null) {
            return major;
        }

        major = majorMapper.selectOne(new QueryWrapper<Major>()
                .eq("id", id)
                .eq("deleted", false)
        );

        if (major == null) {
            throw ServiceException.error(ServiceCode.DataNotExists);
        }

        putCache(major);

        return major;
    }

    public Major getMajor(Long id) {
        return getMajor(id, true);
    }

    public Major getMajorByName(String name) {
        return majorNameCache.get(name, key -> majorMapper.selectOne(new QueryWrapper<Major>()
                        .eq("name", name)
                        .eq("deleted", false)
                        .last("limit 1")
                )
        );
    }

    public Set<Major> listAllMajor() {
        var cacheMap = majorCache.asMap();

        if (!cacheMap.isEmpty()) {
            return new HashSet<>(cacheMap.values());
        }

        var results = majorMapper.selectList(new QueryWrapper<Major>()
                .select("id", "name")
                .eq("deleted", false)
        );

        for (var result : results) {
            putCache(result);
        }

        return new HashSet<>(results);
    }

    private void putCache(Major major) {
        majorCache.put(major.getId(), major);
        majorNameCache.put(major.getName(), major);
    }
}
