package com.pingcap.community.blogserver.service;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.pingcap.community.blogserver.config.OSSConfig;
import com.pingcap.community.blogserver.dto.UploadFileDTO;
import com.pingcap.community.blogserver.entity.User;
import com.pingcap.community.blogserver.vo.upload.AuthUploadVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpServerErrorException;

import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.Set;

@Service
@Slf4j
public class UploadService {

  private final OSSConfig ossConfig;

  private final OSS ossClient;

  public UploadService(OSSConfig ossConfig, OSS ossClient) {
    this.ossConfig = ossConfig;
    this.ossClient = ossClient;
  }

  public AuthUploadVO uploadImage(User uploader, UploadFileDTO uploadFileDTO) {
    String fileName = encryptFileName(uploadFileDTO.getFilename(), uploadFileDTO.getContentType());
    String bucketName = ossConfig.getBucketName();
    String directory = ossConfig.getDirectory();
    String publicBaseURL = ossConfig.getPublicBaseUrl();
    String key = String.format("%s/%s", directory, fileName);
    String publicURL = String.format("%s/%s", publicBaseURL, key);

    GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucketName, key, HttpMethod.PUT);
    urlRequest.setExpiration(DateUtils.addMinutes(new Date(), 30));
    urlRequest.addHeader("x-oss-object-acl", "public-read");
    urlRequest.addHeader("Content-Type", uploadFileDTO.getContentType());
    URL url = ossClient.generatePresignedUrl(urlRequest);

    AuthUploadVO authUploadVO = new AuthUploadVO();
    authUploadVO.setUploadURL(url.toString());
    authUploadVO.setDownloadURL(publicURL);
    return authUploadVO;
  }

  private String encryptFileName(String fileName, String contentType) {
    String suffix = null;
    String namePart = null;

    // Try to match with allow file type.
    String[] allowFileType = ossConfig.getAllowFileType();
    if (allowFileType != null) {
      for (String type : allowFileType) {
        if (StringUtils.endsWithIgnoreCase(fileName, type)) {
          suffix = type;
          namePart = fileName.substring(0, fileName.length() - type.length());
          break;
        }
      }
    }

    // Try to match with allow content type.
    Map<String, String> allowContentTypes = ossConfig.getAllowContentType();
    if (namePart == null) {
      Set<Map.Entry<String, String>> entries = allowContentTypes.entrySet();
      for (Map.Entry<String, String> entry : entries) {
        String allowContentType = entry.getKey();
        String fileSuffix = entry.getValue();
        if (allowContentType.equals(contentType)) {
          suffix = fileSuffix;
          namePart = fileName;
          break;
        }
      }
    }

    if (namePart == null || namePart.length() == 0) {
      throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "Illegal file format.");
    }

    // Notice: Avoid escape characters `<` and `>` being added to the links due to spaces
    // when converting to Markdown format, because some markdown editors don't support this behavior.
    //
    // Reference: https://superuser.com/a/1517072
    namePart = namePart.replaceAll("\s", "");

    long timestamp = new Date().getTime();
    return String.format("%s-%d%s", namePart, timestamp, suffix);
  }

}
