package cn.reghao.tnb.oss.store.controller;

import cn.reghao.tnb.common.web.ServletUtil;
import cn.reghao.tnb.oss.api.constant.ChannelAction;
import cn.reghao.tnb.oss.api.constant.ObjectScope;
import cn.reghao.tnb.oss.api.dto.ObjectChannel;
import cn.reghao.tnb.oss.api.dto.ObjectMeta;
import cn.reghao.tnb.oss.store.db.repository.ObjectRepository;
import cn.reghao.tnb.oss.store.service.ConsoleServiceWrapper;
import cn.reghao.tnb.oss.store.service.GetObjectService;
import cn.reghao.tnb.auth.JwtUtil;
import cn.reghao.tnb.oss.store.util.ObjectUtil;
import cn.reghao.tnb.oss.store.util.SignatureUtil;
import cn.reghao.tnb.auth.model.OssPayload;
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.web.bind.annotation.*;

import jakarta.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 ConsoleServiceWrapper consoleServiceWrapper;
    private final Cache<String, String> cache;

    public ObjectGetController(GetObjectService getObjectService, ObjectRepository objectRepository,
                               ConsoleServiceWrapper consoleServiceWrapper, Cache<String, String> cache) {
        this.getObjectService = getObjectService;
        this.objectRepository = objectRepository;
        this.consoleServiceWrapper = consoleServiceWrapper;
        this.cache = cache;
    }

    // 使用 HEAD 方法判断对象是否存在
    @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);
        }
    }

    // 通过 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();
        String domain = ServletUtil.getHeader("host");
        int owner = consoleServiceWrapper.getNodeProperties(domain).getOwner();
        ObjectMeta objectMeta = objectRepository.getObjectMetaByName(objectName, owner);
        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;
        }

        if (token == null || timestamp == null || nonce == null || sign == null) {
            String payload = "sign invalid";
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN, payload);
            return;
        }

        String secretKey = cache.getIfPresent(token);
        if (secretKey == null) {
            String payload = "secretKey not found";
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN, payload);
            return;
        }

        String queryString = String.format("token=%s&t=%s&nonce=%s", token, timestamp, nonce);
        String host = ServletUtil.getHeader("host");
        String url = String.format("//%s/%s", host, objectName);
        String requestString = String.format("%s%s?%s", "GET", url, queryString);
        boolean valid = SignatureUtil.valid(requestString, secretKey, 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, secretKey);
        int loginUser = ossPayload.getUserId();
        int channelCode = ossPayload.getChannelCode();
        ObjectChannel objectChannel = consoleServiceWrapper.getChannelByCode(loginUser, channelCode);
        if (objectChannel == null) {
            String payload = String.format("channel_id %s not exist", channelCode);
            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN, payload);
            return;
        }

        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);
        }
    }
}
