package io.loli.sc.server.action;

import io.loli.sc.server.entity.ClientToken;
import io.loli.sc.server.entity.Gallery;
import io.loli.sc.server.entity.UploadedImage;
import io.loli.sc.server.entity.User;
import io.loli.sc.server.service.BucketService;
import io.loli.sc.server.service.ClientTokenService;
import io.loli.sc.server.service.FileFetchService;
import io.loli.sc.server.service.GalleryService;
import io.loli.sc.server.service.UploadedImageService;
import io.loli.sc.server.service.UserService;
import io.loli.sc.server.storage.StorageUploader;
import io.loli.util.string.MD5Util;
import io.loli.util.string.ShortUrl;

import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;

/**
 * 负责客户端登录验证和图片上传的类
 * 
 * @author choco
 * 
 */
@Named
@RequestMapping(value = { "/api" })
public class ImageClientUpload {
    @Inject
    private ClientTokenService cts;

    @Inject
    @Named("userService")
    private UserService us;

    @Inject
    private UploadedImageService uic;

    @Inject
    private GalleryService gs;

    @Inject
    @Named("fileFetchService")
    private FileFetchService ffs;

    private Logger logger = Logger.getLogger(ImageClientUpload.class);

    private static final String LOCAL_HOST = "127.0.0.1";

    @RequestMapping(value = { "/token" }, method = { RequestMethod.GET, RequestMethod.POST })
    @ResponseStatus(HttpStatus.OK)
    public @ResponseBody ClientToken requestToken(@RequestParam(required = true) String email,
        @RequestParam(required = true) String password) {
        User trueUser = us.findByEmail(email);
        String token = null;
        ClientToken ct = null;
        // 验证密码是否正确
        if (trueUser != null && trueUser.getPassword().equalsIgnoreCase(password)) {
            ct = cts.findByEmail(email);

            if (ct != null) {
                // 当已有该email的token时，把token返回
                token = ct.getToken();
                logger.info(email + "已有token，将已经存在的token返回");
            } else {
                // 当没有该email的token时，新建一个token保存至数据库，然后返回
                ct = new ClientToken();
                // 用于md5加密的密文
                String word = trueUser.getEmail() + new java.util.Date().getTime();
                try {
                    token = MD5Util.hash(word);
                } catch (NoSuchAlgorithmException e) {
                    logger.error(e);
                }
                ct.setToken(token);
                ct.setUser(trueUser);
                cts.save(ct);
                logger.info(email + "生成新token");
            }
            return ct;
        }
        return new ClientToken();
    }

    @Inject
    private BucketService bucketService;
    @Inject
    private UserService userService;

    @RequestMapping(value = { "/upload" }, method = { RequestMethod.POST })
    @ResponseStatus(HttpStatus.OK)
    public @ResponseBody UploadedImage upload(@RequestParam(value = "token", required = false) String token,
        @RequestParam(value = "email", required = false) String email,
        @RequestParam(value = "desc", required = false) String desc,
        @RequestParam(value = "image", required = true) MultipartFile imageFile, HttpServletRequest request,
        @RequestParam(value = "type", required = false) String type,
        @RequestParam(value = "gid", required = false) Integer gid) {

        UploadedImage imageObj = new UploadedImage();
        imageObj.setDate(new Date());
        if (email == null && desc == null && token == null) {
            imageObj.setDesc(imageFile.getOriginalFilename());
        } else {
            if (!cts.checkTokenBelongToUser(token, email)) {
                logger.info(email + "使用错误的token上传");
                return new UploadedImage();
            } else {
                imageObj.setUser(userService.findByEmail(email));
            }
            imageObj.setOriginName(desc);
        }
        User user = null;
        if ((user = (User) request.getSession().getAttribute("user")) != null) {
            imageObj.setUser(user);
        }

        if (gid != null) {
            Gallery gal = gs.findById(gid);
            imageObj.setGallery(gal);
        }

        String ip = request.getRemoteAddr();
        if (ip != null && LOCAL_HOST.equals(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        imageObj.setIp(ip);
        imageObj.setUa(request.getHeader("user-agent"));

        String contentType = imageFile.getContentType();
        if (StringUtils.isNotBlank(contentType)) {
            imageObj.setContentType(contentType);
        }
        String fileName = "";
        fileName = getFileName(imageFile);

        File file = saveImage(imageFile, fileName);
        String ref = null;
        if (StringUtils.isNotBlank(type)) {
        } else if ((ref = request.getHeader("REFERER")) != null) {
            if (ref.contains("file")) {
                imageObj.setStorageBucket(bucketService.randomFileBucket());
            } else {
                imageObj.setStorageBucket(bucketService.randomImageBucket());
            }
        } else {
            imageObj.setStorageBucket(bucketService.randomImageBucket());
        }

        StorageUploader uploader = StorageUploader.newInstance(imageObj.getStorageBucket());
        imageObj.setPath(uploader.upload(file));
        imageObj.setOriginName(imageFile.getOriginalFilename());

        imageObj.setGeneratedName(file.getName());
        imageObj.setRedirectCode(file.getName());
        imageObj.setGeneratedCode(file.getName().contains(".") ? file.getName().substring(0,
            file.getName().indexOf(".")) : file.getName());
        imageObj.setInternalPath(imageObj.getStorageBucket().getInternalUrl() + "/" + file.getName());

        uic.save(imageObj);
        if (imageObj.getUser() == null) {
            logger.info("匿名上传文件:" + imageObj.getOriginName() + ", 链接为" + imageObj.getPath());
        } else {
            logger.info(imageObj.getUser().getEmail() + "上传文件:" + imageObj.getOriginName() + ", 链接为"
                + imageObj.getPath());
        }

        service.execute(() -> {
            uic.updateThumbnail(imageObj, file, uploader);
        });
        return imageObj;
    }

    private String getFileName(MultipartFile imageFile) {
        String str = null;

        try {
            String[] urls = ShortUrl.shortText(new Date().getTime() + imageFile.getOriginalFilename(), 6);
            for (String url : urls) {
                if (!uic.checkExists(url)) {
                    return url;
                }
            }

            str = MD5Util.hash(String.valueOf(System.nanoTime()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    private ExecutorService service = Executors.newFixedThreadPool(50);

    @ResponseBody
    @RequestMapping(value = { "/fetch" }, method = { RequestMethod.POST })
    public String fetch(@RequestParam(value = "path") String path, HttpServletRequest request) {
        if (StringUtils.isBlank(path)) {
            return "{\"origin\":\"" + path + "\",\"error\":\"" + "图片url不能为空" + "\",\"redirect\":\"" + "\"}";
        }
        File file = null;
        Future<File> future = service.submit(() -> {
            return ffs.fetch(path);
        });
        try {
            file = future.get(20, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            e.printStackTrace();
            logger.error("Fetch image timeout:" + e);
            return "{\"origin\":\"" + path + "\",\"error\":\"" + e.getMessage() + "\",\"redirect\":\"" + "\"}";
        }

        if (file == null) {
            return "";
        }
        try {
            User user = null;
            UploadedImage imageObj = new UploadedImage();
            if ((user = (User) request.getSession().getAttribute("user")) != null) {
                imageObj.setUser(user);
            }
            String ip = request.getRemoteAddr();
            if (ip != null && ip.equals("127.0.0.1")) {
                ip = request.getHeader("X-Real-IP");
            }
            imageObj.setIp(ip);
            imageObj.setUa(request.getHeader("user-agent"));
            String ref = null;
            if ((ref = request.getHeader("REFERER")) != null) {
                if (ref.contains("file")) {
                    imageObj.setStorageBucket(bucketService.randomFileBucket());
                } else {
                    imageObj.setStorageBucket(bucketService.randomImageBucket());
                }
            } else {
                imageObj.setStorageBucket(bucketService.randomImageBucket());
            }

            StorageUploader uploader = StorageUploader.newInstance(imageObj.getStorageBucket());
            imageObj.setPath(uploader.upload(file));
            imageObj.setOriginName(file.getName());

            imageObj.setGeneratedName(file.getName());
            imageObj.setRedirectCode(file.getName());
            imageObj.setGeneratedCode(file.getName().contains(".") ? file.getName().substring(0,
                file.getName().indexOf(".")) : file.getName());

            imageObj.setInternalPath(imageObj.getStorageBucket().getInternalUrl() + "/" + file.getName());

            uic.save(imageObj);
            if (imageObj.getUser() == null) {
                logger.info("匿名上传文件:" + imageObj.getOriginName() + ", 链接为" + imageObj.getPath());
            } else {
                logger.info(imageObj.getUser().getEmail() + "上传文件:" + imageObj.getOriginName() + ", 链接为"
                    + imageObj.getPath());
            }
            return "{\"origin\":\"" + path + "\",\"error\":\"" + "\",\"redirect\":\"" + imageObj.getRedirectCode()
                + "\"}";
        } catch (Exception e) {
            return "{\"origin\":\"" + path + "\",\"error\":\"" + e.getMessage() + "\",\"redirect\":\"" + "\"}";
        }
    }

    /**
     * 将一个图片保存起来
     * 
     * @param image
     * @return 保存后的图片File对象
     */
    private File saveImage(MultipartFile image, String fileName) {
        if (image.getOriginalFilename().contains(".")) {
            // 获取图片扩展名，jpg,png
            fileName += "."
                + image.getOriginalFilename().substring(image.getOriginalFilename().lastIndexOf(".") + 1).toLowerCase();
        }

        File file = new File(System.getProperty("java.io.tmpdir"), fileName);

        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdir();
        }
        try {
            FileUtils.writeByteArrayToFile(file, image.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
            logger.error(e);
        }
        return file;
    }
}
