package com.lion.lionwebsite.Util;

import com.lion.lionwebsite.Domain.Gallery;
import com.lion.lionwebsite.Exception.ResolutionNotMatchException;
import org.apache.http.HttpEntity;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class GalleryUtil {

    static String POST = "post";
    static String GET = "get";

    static String FORM_DATA = "formData";

    static String JSON = "json";

    public static ArrayList<Gallery> galleriesForDownload;

    static {
        galleriesForDownload = new ArrayList<>();
    }


    /**
     * 解析/下载本子
     * @param url  本子链接
     * @param isDownload    是否下载 否则仅解析
     * @param targetResolution  目标下载分辨率 不下载时改值为空
     * @return  解析/下载的本子对象
     * @throws IOException      io问题
     * @throws ResolutionNotMatchException  没有目标分辨率
     */
    public static Gallery parse(String url, boolean isDownload, String targetResolution) throws IOException, ResolutionNotMatchException {
        String origin = verifyLink(url);

        if(origin == null){
            return null;
        }

        //初始化本子
        Gallery gallery = new Gallery();
        gallery.setLink(url);
        gallery.setCreateTime(System.currentTimeMillis()/1000);
        gallery.setGid(Integer.parseInt(url.split("/")[4]));
        gallery.setProceeding(0);

        //访问本子页面
        String galleryPage = requests(url, GET, null, null);
        Document galleryDoc = Jsoup.parse(galleryPage);

        //收集本子基本信息
        gallery.setName(galleryDoc.select("#gn").text() + " [" + gallery.getGid() + ']');
        gallery.setLanguage(galleryDoc.select("#gdd > table > tbody > tr:nth-child(4) > td.gdt2").text().replace("%nbps", ""));
        gallery.setPages(Integer.parseInt(galleryDoc.select("#gdd > table > tbody > tr:nth-child(6) > td.gdt2").text().split(" ")[0]));
        gallery.setFileSize(CustomUtil.stringToFileSize(galleryDoc.select("#gdd > table > tbody > tr:nth-child(5) > td.gdt2").text().replace(" ", "")));
        gallery.setDisplayFileSize(CustomUtil.fileSizeToString(gallery.getFileSize()));

        //查找下载页面链接并初始化参数
        String download_link = galleryDoc.select("#gd5 > p:nth-child(2) > a").attr("onclick").split("'")[1];
        HashMap<String, String> headers = new HashMap<>();
        headers.put("origin", origin);
        headers.put("referer", download_link);

        //访问下载页面，获取分辨率
        String downloadPage = requests(download_link, GET, headers, null);
        Document downloadDoc = Jsoup.parse(downloadPage);
        Elements resolutions = downloadDoc.select("#db > div > table > tbody > tr > td");
        download_link = downloadDoc.select("#hathdl_form").attr("action");

        Map<String, String> availableResolution = new LinkedHashMap<>();
        Map<String, String> tempResolutions = new LinkedHashMap<>();
        ArrayList<Integer> tempResolutionKeys = new ArrayList<>();

        for(Element element: resolutions){
            String[] parameter = element.select("td > p").text().split(" ");
            if(parameter[1].contains("N/A"))
                continue;
            tempResolutions.put(parameter[0], parameter[1] + parameter[2]);
            if(parameter[0].contains("x")){
                tempResolutionKeys.add(Integer.valueOf(parameter[0].replace("x", "")));
            }
        }

        tempResolutionKeys.sort(Comparator.comparingInt(o -> o));

        for(Integer resolution: tempResolutionKeys){
            availableResolution.put(resolution.toString()+"x", tempResolutions.get(resolution+"x"));
        }

        availableResolution.put("Original", tempResolutions.get("Original"));
        gallery.setAvailableResolution(availableResolution);

        //如果不是下载，则直接返回
        if(!isDownload){
            gallery.setStatus("等待确认下载");
            return gallery;
        }

        //如果目标分辨率不存在，抛出错误
        if(!gallery.getAvailableResolution().containsKey(targetResolution)){
            System.out.println(gallery.getAvailableResolution());
            throw new ResolutionNotMatchException(targetResolution);
        }

        gallery.setResolution(targetResolution);
        gallery.setFileSize(CustomUtil.stringToFileSize(availableResolution.get(targetResolution).replace(" ", "")));
        gallery.setDisplayFileSize(CustomUtil.fileSizeToString(gallery.getFileSize()));

        //提交下载请求
        HashMap<String, String> body = new HashMap<>();
        body.put("payload", FORM_DATA);
        body.put("hathdl_xres", targetResolution.replace("x", "").replace("Original", "org"));
        downloadPage = requests(download_link, POST, headers, body);
        downloadDoc = Jsoup.parse(downloadPage);

        //判断下载请求是否提交成功
        if(downloadDoc.select("#db > p:nth-child(2) > strong").text().startsWith("#"))
            gallery.setStatus("已提交");
        else {
            System.out.println(downloadDoc.select("#db"));
            gallery.setStatus("提交失败");
        }

        return gallery;
    }


    /**
     * 验证链接
     * @param url 链接
     * @return origin
     */
    public static String verifyLink(String url){
        if(url == null)
            return null;
        if(url.length() < 40)
            return null;

        if(!url.contains("/g/") || !url.contains("exhentai"))
            return null;
        return "";
    }

    /**
     * 自用request
     * @param url 链接
     * @param headers 额外参数
     * @param method 请求方法
     * @return 请求页面
     * @throws IOException 可能会抛出IO错误
     */
    public static String requests(String url, String method, HashMap<String, String> headers, HashMap<String, String> body) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse httpResponse;
        if(headers == null)
            headers = new HashMap<>();

        headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0");
        headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        headers.put("Upgrade-Insecure-Requests", "1");
        headers.put("Cookie", "ipb_member_id=5774855; ipb_pass_hash=4b061c3abe25289568b5a8e0123fb3b9; igneous=cea2e08fb; sk=oye107wk02gtomb56x65dmv4qzbn; nw=1");

        if(method.equals(GET)){
            HttpGet httpGet = new HttpGet(url);
            for (Map.Entry<String, String> header: headers.entrySet())
                httpGet.addHeader(header.getKey(), header.getValue());

            httpResponse = httpClient.execute(httpGet);
        } else {
            HttpPost httpPost = new HttpPost(url);
            for (Map.Entry<String, String> header: headers.entrySet())
                httpPost.addHeader(header.getKey(), header.getValue());

            if(body != null) {
                String payload;
                if((payload = body.remove("payload")).equals(JSON)) {
                    EntityBuilder entityBuilder = EntityBuilder.create();
                    entityBuilder.setContentType(ContentType.APPLICATION_JSON);
                    entityBuilder.setText(CustomUtil.objectMapper.writeValueAsString(body));
                    httpPost.setEntity(entityBuilder.build());
                } else if(payload.equals(FORM_DATA)) {
                    MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
                    body.forEach(multipartEntityBuilder::addTextBody);
                    httpPost.setEntity(multipartEntityBuilder.build());
                }
            }
            httpResponse = httpClient.execute(httpPost);
        }
        HttpEntity responseEntity = httpResponse.getEntity();
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        StringBuilder stringBuilder = new StringBuilder();

        if(statusCode == 200){
            BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
            String str;
            while((str = reader.readLine()) != null)
                stringBuilder.append(str).append("\n");
        }
        else{
            System.out.println(statusCode);
        }

        httpClient.close();
        httpResponse.close();

        return stringBuilder.toString();
    }
}
