package cn.reghao.oss.store.controller;

import cn.reghao.oss.storeapi.constant.ChannelAction;
import cn.reghao.oss.storeapi.constant.ObjectScope;
import cn.reghao.oss.storeapi.dto.ObjectChannel;
import cn.reghao.oss.storeapi.dto.ObjectMeta;
import cn.reghao.oss.store.db.repository.ObjectRepository;
import cn.reghao.oss.store.service.GetObjectService;
import cn.reghao.oss.store.service.StoreLocalService;
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.storeapi.dto.OssPayload;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author reghao
 * @date 2022-12-08 20:38:33
 */
@RestController
public class ObjectGetController {
    private final GetObjectService getObjectService;
    private final ObjectRepository objectRepository;
    private final StoreLocalService storeLocalService;

    public ObjectGetController(GetObjectService getObjectService, ObjectRepository objectRepository, 
                               StoreLocalService storeLocalService) {
        this.getObjectService = getObjectService;
        this.objectRepository = objectRepository;
        this.storeLocalService = storeLocalService;
    }

    @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();
        ObjectMeta objectMeta = objectRepository.getObjectMetaByName(objectName);
        if (objectMeta == null) {
            getObjectService.writeResponse(HttpServletResponse.SC_NOT_FOUND);
            return;
        } else if (client != null && !client.isBlank()) {
            getObjectService.getObject(objectMeta);
            return;
        } else if (objectMeta.getScope() == ObjectScope.PUBLIC.getCode()) {
            getObjectService.getObject(objectMeta);
            return;
        }

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

        long current = System.currentTimeMillis();
        if (current > timestamp) {
            String payload = "timestamp invalid";
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN, payload);
            return;
        }

        OssPayload ossPayload = JwtUtil.getOssPayload(token, storeLocalService.getSecretKey());
        int loginUser = ossPayload.getUserId();
        int channelId = ossPayload.getChannelId();
        ObjectChannel objectChannel = storeLocalService.getChannelByChannelId(loginUser, channelId);
        if (objectChannel == null) {
            String payload = String.format("channel_id %s not exist", channelId);
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN, payload);
            return;
        }

        int userId = ossPayload.getUserId();
        String prefix = objectChannel.getPrefix();
        if (!objectName.startsWith(prefix)) {
            String payload = String.format("channel prefix %s not matched", prefix);
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN, payload);
            return;
        }

        String action = ossPayload.getAction();
        if (ChannelAction.access.getName().equals(action)) {
            getObjectService.getObject(objectMeta);
        } else if (ChannelAction.download.getName().equals(action)) {
            getObjectService.writeDownloadContent(objectMeta);
        } else {
            String payload = String.format("channel action %s not matched", action);
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN, payload);
        }
    }
}
