package cn.reghao.oss.store.controller;

import cn.reghao.oss.api.constant.ChannelAction;
import cn.reghao.oss.store.config.OssProperties;
import cn.reghao.oss.store.service.GetObjectService;
import cn.reghao.oss.store.util.JwtUtil;
import cn.reghao.oss.store.util.ObjectUtil;
import cn.reghao.oss.store.util.SignatureUtil;
import cn.reghao.oss.api.constant.UploadChannel;
import cn.reghao.oss.api.dto.OssPayload;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author reghao
 * @date 2022-12-08 20:38:33
 */
@RestController
public class ObjectGetController {
    private final GetObjectService getObjectService;
    private final Cache<String, String> cache;
    private final OssProperties ossProperties;

    public ObjectGetController(GetObjectService getObjectService, OssProperties ossProperties) {
        this.getObjectService = getObjectService;
        this.cache = Caffeine.newBuilder().maximumSize(10_000).expireAfterAccess(1, TimeUnit.HOURS).build();
        this.ossProperties = ossProperties;
    }

    @RequestMapping(value = "/**", method = RequestMethod.HEAD)
    public void headObject(@RequestParam(value = "sha256sum", required = false) String sha256sum) throws IOException {
        if (sha256sum != null) {
            getObjectService.headObject1(sha256sum);
        } else {
            String objectName = ObjectUtil.getObjectName();
            getObjectService.headObject(objectName);
        }
    }

    @GetMapping(value = "/**")
    public void getObject(@RequestParam(value = "token", required = false) String token,
                          @RequestParam(value = "t", required = false) Long timestamp,
                          @RequestParam(value = "nonce", required = false) String nonce,
                          @RequestParam(value = "sign", required = false) String sign,
                          @RequestParam(value = "client", required = false) String client) throws IOException {
        String objectName = ObjectUtil.getObjectName();
        if (client != null && !client.isBlank()) {
            getObjectService.getObject(objectName);
            return;
        }

        if (objectName.startsWith(UploadChannel.avatar.getPrefix())
                || objectName.startsWith(UploadChannel.image.getPrefix())
                || objectName.startsWith(UploadChannel.img.getPrefix())) {
            getObjectService.getObject(objectName);
            return;
        }

        String queryString = String.format("token=%s&t=%s&nonce=%s", token, timestamp, nonce);
        String domain = ossProperties.getDomain();
        String url = String.format("//%s/%s", domain, objectName);
        String requestString = String.format("%s%s?%s", "GET", url, queryString);
        boolean valid = SignatureUtil.valid(requestString, ossProperties.getSecretKey(), sign);
        if (!valid) {
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        long current = System.currentTimeMillis();
        if (current > timestamp) {
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        /*String value = cache.getIfPresent(nonce);
        if (value == null) {
            cache.put(nonce, nonce);
        } else {
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
            return;
        }*/

        OssPayload ossPayload = JwtUtil.getOssPayload(token, ossProperties.getSecretKey());
        int channelId = ossPayload.getChannelId();
        long userId = ossPayload.getUserId();
        String prefix = UploadChannel.getUploadChannel(channelId).getPrefix();
        if (!objectName.startsWith(prefix)) {
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        String action = ossPayload.getAction();
        if (ChannelAction.access.getName().equals(action)) {
            getObjectService.getObject(objectName);
        } else if (ChannelAction.download.getName().equals(action)) {
            getObjectService.downloadObject(objectName);
        } else {
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
        }
    }
}
