package com.lion.lionwebsite.Service;

import com.lion.lionwebsite.Dao.normal.CustomConfigurationMapper;
import com.lion.lionwebsite.Dao.normal.GalleryMapper;
import com.lion.lionwebsite.Dao.normal.ShareFileMapper;
import com.lion.lionwebsite.Domain.CustomConfiguration;
import com.lion.lionwebsite.Domain.ShareFile;
import com.lion.lionwebsite.Util.CustomUtil;
import lombok.Data;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static java.nio.file.FileVisitResult.CONTINUE;

@Service
@Data
public class LocalServiceImpl{
    String DouNaiClash = "https://aaaa.gay/link/X7zEqkIx5gtIGugO?client=clashmeta";

    String DouNaiV2ray = "https://aaaa.gay/link/X7zEqkIx5gtIGugO?client=v2";

    CustomConfigurationMapper configurationMapper;

    ShareFileMapper shareFIleMapper;

    GalleryMapper galleryMapper;

    public LocalServiceImpl(CustomConfigurationMapper configurationMapper, ShareFileMapper shareFileMapper, GalleryMapper galleryMapper){
        this.shareFIleMapper = shareFileMapper;
        this.configurationMapper = configurationMapper;
        this.galleryMapper = galleryMapper;
    }

    /**
     * 每周周一四点重置额度
     */
    @Scheduled(cron = "0 0 4 * * MON")
    public void reset() {
        configurationMapper.updateConfiguration(CustomConfiguration.WEEK_USED_AMOUNT, "0");
        configurationMapper.updateConfiguration(CustomConfiguration.LAST_RESET_AMOUNT_TIME, CustomUtil.now());
    }

    /**
     * 定时更新订阅
     * @throws IOException 下载以及保存异常
     */
    @Scheduled(fixedRate = 86400000)
    public void updateSubScheduler() throws IOException {
        updateSub(false);
    }

    /**
     * 更新订阅链接的实际方法
     */
    public boolean updateSub(boolean isManual) throws IOException {
        DateTimeFormatter dateTimeFormatter = CustomUtil.dateTimeFormatter();
        CustomConfiguration customConfiguration = configurationMapper.selectConfiguration(CustomConfiguration.LAST_UPDATE_SUB_TIME);

        //如果不是手动，则判断更新间隔是否满足，不满足则取消更新
        if (!isManual) {
            LocalDateTime lastUpdate = LocalDateTime.parse(customConfiguration.getValue(), CustomUtil.dateTimeFormatter());
            LocalDateTime now = LocalDateTime.now();
            now = now.plusHours(-3);
            if(now.isBefore(lastUpdate))
                return false;
        }

        File DouNaiClashFile = new File("sub/DouNaiClash.txt");
        File DouNaiV2rayFile = new File("sub/DouNaiV2ray.txt");
        File directory = new File("sub");

        if(!directory.isDirectory())
            Files.createDirectory(Paths.get("sub"));

        List<String> DouNaiClash_profile;

        //下载豆奶v2ray订阅
        try(FileWriter writer = new FileWriter(DouNaiV2rayFile)) {
            String DouNaiV2rayRaw = Get(DouNaiV2ray).getFirst();
            String[] v2rayPlain = new String(Base64.getDecoder().decode(DouNaiV2rayRaw)).split("\n");
            StringBuilder stringBuilder = new StringBuilder();
            //过滤高倍率节点
            for(String node: v2rayPlain){
                String name = URLDecoder.decode(node.split("#")[1], StandardCharsets.UTF_8);
                if(name.startsWith("⛅") && name.contains("流量")){
                    float ratio = Float.parseFloat(name.substring(name.indexOf("(") + 1, name.indexOf(")")).replace("倍流量", ""));
                    if(ratio <= 1)
                        stringBuilder.append(node).append("\n");
                }
                else{
                    stringBuilder.append(node).append("\n");
                }
            }
            writer.write(new String(Base64.getEncoder().encode(stringBuilder.toString().getBytes(StandardCharsets.UTF_8))));

            System.out.println("load DouNai v2ray complete");
        }catch (IOException e){
            e.printStackTrace();
            System.out.println("load DouNai v2ray failure");
        }

        //下载豆奶clash订阅
        try(FileWriter writer = new FileWriter(DouNaiClashFile)) {
            DouNaiClash_profile = Get(DouNaiClash);
            //过滤高倍率节点
            ArrayList<String> clashProcessed = new ArrayList<>();
            boolean isProxies = false;
            boolean skip = false;
            for(String line: DouNaiClash_profile){
                if(line.equals("proxies:"))
                    isProxies = true;
                else if(line.equals("proxy-groups:") && isProxies)
                    isProxies = false;

                if(isProxies) {
                    if (line.contains("name"))
                        skip = line.contains("流量");
                    if (!skip)
                        clashProcessed.add(line);
                }
                else
                    if (!line.contains("流量"))
                        clashProcessed.add(line);
            }

            for(String line: clashProcessed)
                writer.write(line + "\n");
            System.out.println("load DouNai clash complete");
        }catch (IOException e){
            e.printStackTrace();
            System.out.println("load DouNai clash failure");
        }

        configurationMapper.updateConfiguration(CustomConfiguration.LAST_UPDATE_SUB_TIME, dateTimeFormatter.format(LocalDateTime.now()));
        return true;
    }

    /**
     * 封装好的get方法
     * @param url               url
     * @return                  请求结果
     * @throws IOException      网络异常
     */
    public static ArrayList<String> Get(String url) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse httpResponse;
        HttpGet httpGet = new HttpGet(url);

        httpResponse = httpClient.execute(httpGet);

        HttpEntity responseEntity = httpResponse.getEntity();
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        ArrayList<String> temp = new ArrayList<>();

        if (statusCode == 200) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
            String str;
            while ((str = reader.readLine()) != null)
                temp.add(str);
        }

        httpClient.close();
        httpResponse.close();
        return temp;
    }

    /**
     * 每天四点，清理过期分享码
     */
    @Scheduled(cron = "0 0 4 * * *")
    public void checkShareCode(){
        ShareFile[] shareFiles = shareFIleMapper.selectAllShareFile();
        Calendar now;
        Calendar expireTime;
        for(ShareFile shareFile: shareFiles){
            now = Calendar.getInstance();
            expireTime = Calendar.getInstance();
            expireTime.setTime(shareFile.getExpireTime());
            if(now.after(expireTime))
                shareFIleMapper.deleteShareFile(shareFile.getShareCode());
        }
    }

    /**
     * 每周，查看缩略图数量，按照访问时间排序，删除超出1w的部分
     */
    @Scheduled(cron = "0 0 4 1 * *")
    public void clearThumbnailCache(){
        String cachePath = "/storage/hentaiCache/";
        File directory = new File(cachePath);
        List<Path> files = new ArrayList<>();
        try {
            Files.walkFileTree(directory.toPath(), new SimpleFileVisitor<>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    if (attrs.isRegularFile()) {
                        files.add(file);
                    }
                    return CONTINUE;
                }
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        files.sort((f1, f2) -> {
            try {
                return Files.readAttributes(f1, BasicFileAttributes.class).lastAccessTime()
                        .compareTo(Files.readAttributes(f2, BasicFileAttributes.class).lastAccessTime());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });

        if (files.size() > 10000) {
            List<Path> toDelete = files.subList(0, files.size() - 10000);
            for (Path file : toDelete) {
                try {
                    Files.delete(file);
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            System.out.println("Deleted " + toDelete.size() + " files");
        } else {
            System.out.println("No files to delete");
        }
    }
}
