package com.red.circle.external.app.oss;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.CopyObjectRequest;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.region.Region;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.UUID;

/**
 * 腾讯云 COS 存储服务.
 */
@Service
public class TencentCosStorageService {

  private static final String AVATAR_PREFIX = "avatar/";
  private static final Logger log = LoggerFactory.getLogger(TencentCosStorageService.class);

  private final TencentCosProperties properties;

  public TencentCosStorageService(TencentCosProperties properties) {
    this.properties = properties;
  }

  public boolean isEnabled() {
    return properties.isConfigured();
  }

  public String uploadAvatar(InputStream inputStream, long contentLength, String contentType, String suffix) {
    String key = AVATAR_PREFIX + UUID.randomUUID() + suffix;
    upload(key, inputStream, contentLength, contentType);
    return buildAccessUrl(key);
  }

  public void upload(String key, InputStream inputStream, long contentLength, String contentType) {
    COSClient client = buildClient();
    try {
      byte[] data = inputStream.readAllBytes();
      ObjectMetadata metadata = new ObjectMetadata();
      metadata.setContentLength(data.length);
      if (StringUtils.hasText(contentType)) {
        metadata.setContentType(contentType);
      }
      client.putObject(
          new PutObjectRequest(
              properties.getBucketName(), key, new ByteArrayInputStream(data), metadata));
    } catch (IOException e) {
      log.error("tencent cos read upload stream failed key={}", key, e);
      throw new IllegalStateException("read upload stream failed", e);
    } catch (Exception e) {
      log.error("tencent cos upload failed key={}", key, e);
      throw e;
    } finally {
      client.shutdown();
    }
  }

  public boolean isManagedUrl(String source) {
    if (!isEnabled() || !StringUtils.hasText(source)) {
      return false;
    }
    if (source.startsWith(getAccessUrlPrefix())) {
      return true;
    }
    return source.contains(properties.getBucketName() + ".cos." + properties.getRegion() + ".myqcloud.com/");
  }

  public String getAccessUrl(String keyOrUrl) {
    if (!StringUtils.hasText(keyOrUrl)) {
      return keyOrUrl;
    }
    if (looksLikeUrl(keyOrUrl)) {
      return keyOrUrl;
    }
    return buildAccessUrl(keyOrUrl);
  }

  public void remove(String url) {
    String key = resolveKey(url);
    if (!StringUtils.hasText(key)) {
      return;
    }
    COSClient client = buildClient();
    try {
      client.deleteObject(properties.getBucketName(), key);
    } catch (Exception e) {
      log.error("tencent cos remove failed url={}", url, e);
      throw e;
    } finally {
      client.shutdown();
    }
  }

  public void processFileSaveAs(String source, String target) {
    if (!StringUtils.hasText(target)) {
      return;
    }
    copyObject(source, target);
  }

  public String processImgSaveAsCompressZoom(String source, String target, int height) {
    if (!StringUtils.hasText(target)) {
      return processImgSaveAsCompressZoom(source, height);
    }
    copyObject(source, target);
    return buildAccessUrl(target);
  }

  public String processImgSaveAsCompressZoom(String source, int height) {
    return getAccessUrl(source);
  }

  private void copyObject(String source, String target) {
    String sourceKey = resolveKey(source);
    if (!StringUtils.hasText(sourceKey)) {
      return;
    }
    COSClient client = buildClient();
    try {
      CopyObjectRequest request =
          new CopyObjectRequest(properties.getBucketName(), sourceKey, properties.getBucketName(), target);
      client.copyObject(request);
    } catch (Exception e) {
      log.error("tencent cos copy failed source={} target={}", source, target, e);
      throw e;
    } finally {
      client.shutdown();
    }
  }

  private String resolveKey(String keyOrUrl) {
    if (!StringUtils.hasText(keyOrUrl)) {
      return keyOrUrl;
    }
    if (!looksLikeUrl(keyOrUrl)) {
      return keyOrUrl;
    }
    if (!isManagedUrl(keyOrUrl)) {
      return null;
    }
    String path = URI.create(keyOrUrl).getPath();
    if (!StringUtils.hasText(path)) {
      return null;
    }
    return path.startsWith("/") ? path.substring(1) : path;
  }

  private String buildAccessUrl(String key) {
    return getAccessUrlPrefix() + key;
  }

  private String getAccessUrlPrefix() {
    String accessUrl = StringUtils.hasText(properties.getAccessUrl())
        ? properties.getAccessUrl()
        : "https://" + properties.getBucketName() + ".cos." + properties.getRegion() + ".myqcloud.com";
    return accessUrl.endsWith("/") ? accessUrl : accessUrl + "/";
  }

  private boolean looksLikeUrl(String value) {
    return value.startsWith("http://") || value.startsWith("https://");
  }

  private COSClient buildClient() {
    COSCredentials credentials =
        new BasicCOSCredentials(properties.getSecretId(), properties.getSecretKey());
    ClientConfig clientConfig = new ClientConfig(new Region(properties.getRegion()));
    clientConfig.setHttpProtocol(HttpProtocol.https);
    clientConfig.setConnectionTimeout(properties.getConnectionTimeoutMs());
    clientConfig.setConnectionRequestTimeout(properties.getConnectionRequestTimeoutMs());
    clientConfig.setSocketTimeout(properties.getSocketTimeoutMs());
    clientConfig.setRequestTimeOutEnable(true);
    clientConfig.setRequestTimeout(properties.getRequestTimeoutMs());
    clientConfig.setShutdownTimeout(properties.getShutdownTimeoutMs());
    clientConfig.setMaxErrorRetry(properties.getMaxErrorRetry());
    return new COSClient(credentials, clientConfig);
  }
}
