package com.apobates.forum.utils;

import com.github.jscookie.javacookie.Cookies;
import com.github.jscookie.javacookie.Cookies.Attributes;
import com.github.jscookie.javacookie.Expiration;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Objects;
import java.util.Optional;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.joda.time.DateTimeZone;

/**
 * @see https://github.com/js-cookie/java-cookie
 * @author xiaofanku
 * @since 20200511
 */
public final class CookieUtils {
    /**
     * 序列化会员信息到Cookie中 
     *
     * @param cookieValue cookie中保存的值
     * 值不应包含空格，方括号，括号，等号，逗号，双引号，斜杠，问号，符号，冒号和分号。空值在所有浏览器上的行为可能并不相同。 values should
     * not contain white space, brackets, parentheses, equals signs, commas,
     * double quotes, slashes, question marks, at signs, colons, and semicolons.
     * Empty values may not behave the same way on all browsers.
     * @param cookieMaxAge 时长,-1表示浏览器关闭cookie失效,以秒为单位
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieName cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     * @param isHttps work on HTTPS/true,false work on http
     */
    public static void serializeCookie(
            String cookieValue,
            int cookieMaxAge,
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieName,
            String cookiePath,
            String cookieDomain,
            boolean isHttps) throws IllegalStateException {
        Cookies cookies = Cookies.initFromServlet(request, response);
        cookies.set(cookieName, cookieValue, Attributes.empty().expires(Expiration.days(1)).httpOnly(isHttps).path(cookiePath).domain(cookieDomain));
    }
    /**
     * 序列化会员信息到Cookie中 
     * 
     * @param cookieValue cookie中保存的值
     * @param expireDateTime 过期的日期
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieName cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     * @param isHttps work on HTTPS/true,false work on http
     * @throws IllegalStateException 
     */
    public static void serializeCookie(
            String cookieValue,
            LocalDateTime expireDateTime,
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieName,
            String cookiePath,
            String cookieDomain,
            boolean isHttps) throws IllegalStateException {
        org.joda.time.DateTime expireJodaDT = toJodaDateTime(expireDateTime);
        Cookies cookies = Cookies.initFromServlet(request, response);
        cookies.set(cookieName, cookieValue, Attributes.empty().expires(Expiration.date(expireJodaDT)).httpOnly(isHttps).path(cookiePath).domain(cookieDomain));
    }
    /**
     * Convert {@link java.time.LocalDate} to {@link org.joda.time.DateTime}
     */
    private static org.joda.time.DateTime toJodaDateTime(LocalDateTime localDate) {
        return new org.joda.time.DateTime(DateTimeZone.UTC).withDate(
                localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()
        ).withTime(localDate.getHour(), localDate.getMinute(), localDate.getSecond(), 0);
    }
    /**
     * 清空Cookie中的会员信息
     *
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieName cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     * @param isHttpOnly 是否只限Http(JS不可以访问),true是
     */
    public static void expireCookie(
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieName,
            String cookiePath,
            String cookieDomain,
            boolean isHttpOnly) {
        Cookies cookies = Cookies.initFromServlet(request, response);
        cookies.remove(cookieName, Attributes.empty().path(cookiePath).httpOnly(isHttpOnly).domain(cookieDomain));
    }
    
    /**
     * 清空Cookie中的会员信息,只限Http(JS不可以访问)
     * 
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieName cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     */
    public static void expireCookie(
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieName,
            String cookiePath,
            String cookieDomain) {
        expireCookie(request, response, cookieName, cookiePath, cookieDomain, true);
    }
    
    public static void expireCookie(
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieName) {
        Cookies cookies = Cookies.initFromServlet(request, response);
        cookies.remove(cookieName);
    }
    
    /**
     * 获取请求中指定的Cookie
     *
     * @param request Http请求对象
     * @param cookieName cookie的Key
     * @return
     */
    public static Optional<Cookie> queryCookie(HttpServletRequest request, String cookieName) {
        Objects.requireNonNull(request);
        Cookie data = null;
        Cookie[] cookies = request.getCookies();
        
        if (null != cookies) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(cookieName)) {
                    data = cookie;
                    break;
                }
            }
        }
        return Optional.ofNullable(data);
    }
    
    /**
     * Cookie的值中是否存在非法字符
     *
     * @see org.apache.tomcat.util.http.Rfc6265CookieProcessor
     * @param value
     * @return 存在非法字符产生异常,不存在返回true
     * @exception IllegalArgumentException
     */
    public static boolean validateCookieValue(String value) throws IllegalArgumentException {
        int start = 0;
        int end = value.length();
        if (end > 1 && value.charAt(0) == '"' && value.charAt(end - 1) == '"') {
            start = 1;
            end--;
        }
        char[] chars = value.toCharArray();
        for (int i = start; i < end; i++) {
            char c = chars[i];
            if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c || c == 0x7f) {
                throw new IllegalArgumentException(String.format("An invalid character %s", Integer.toString(c)));
            }
        }
        return true;
    }
    
    /**
     * 使用BASE64编码Cookie中保存的值
     *
     * @param value 保存在Cookie中的值
     * @param defaultValue 若编码失败使用此值
     * @return
     */
    public static String encodeCookieValue(String value, String defaultValue) {
        try{
            return Base64.getEncoder().encodeToString(value.getBytes(StandardCharsets.UTF_8.name()));
        }catch(NullPointerException | UnsupportedEncodingException ex){
            return defaultValue;
        }
    }
    
    /**
     * 使用BASE64解码Cookie中保存的值
     *
     * @param value 保存在Cookie中的值
     * @param defaultValue 若解码失败使用此值
     * @return
     */
    public static String decodeCookieValue(String value, String defaultValue) {
        try {
            byte[] dataBytes = Base64.getDecoder().decode(value);
            return new String(dataBytes, StandardCharsets.UTF_8.name());
        } catch (NullPointerException | UnsupportedEncodingException ex) {
            return defaultValue;
        }
    }
}
