package com.gxwebsoft.common.system.controller;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.utils.FileServerUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.Company;
import com.gxwebsoft.common.system.entity.FileRecord;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.service.CompanyService;
import com.gxwebsoft.common.system.service.FileRecordService;
import com.gxwebsoft.common.system.service.SettingService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 阿里云OSS云存储
 *
 * @author WebSoft
 * @since 2022-11-19 13:54:27
 */
@Tag(name = "阿里云OSS")
@RestController
@RequestMapping("/api/oss")
public class AliOssController extends BaseController {
  @Resource
  private ConfigProperties config;
  @Resource
  private RedisUtil redisUtil;
  @Resource
  private FileRecordService fileRecordService;
  @Resource
  private CompanyService companyService;
  @Resource
  private SettingService settingService;

  @Operation(summary = "上传文件")
  @PostMapping("/upload")
  public ApiResult<FileRecord> upload(@RequestParam MultipartFile file, HttpServletRequest request) throws Exception{
    // 获取租户ID
    String tenantId = request.getHeader("TenantId");
    String companyId = request.getHeader("CompanyId");
    String merchantId = request.getHeader("MerchantId");
    String groupId = request.getHeader("GroupId");
    String appId = request.getHeader("AppId");
    if(StrUtil.isBlank(tenantId)){
      return fail("传参错误",null);
    }

    // 读取配置信息
    JSONObject settingInfo;
    String key3 = "Upload:" + tenantId;
    settingInfo = redisUtil.get(key3, JSONObject.class);
    if (ObjectUtil.isEmpty(settingInfo)) {
      settingInfo = settingService.getBySettingKey("upload");
      if (ObjectUtil.isNotEmpty(settingInfo)) {
        redisUtil.set(key3,settingInfo);
      }else {
        return fail("请先配置云存储",null);
      }
    }
    String endpoint = settingInfo.getString("bucketEndpoint");
    String bucketDomain = settingInfo.getString("bucketDomain");
    String bucketName = settingInfo.getString("bucketName");
    String accessKeyId = settingInfo.getString("accessKeyId");
    String accessKeySecret = settingInfo.getString("accessKeySecret");
    String uploadMethod = settingInfo.getString("uploadMethod");

    // 判断是否登录
    String authorization = getAuthorization();

    // 判断存储空间是否已满
    String key = "StorageIsFull:" + tenantId;
    String storageIsFull = redisUtil.get(key);
    if(StrUtil.isNotBlank(storageIsFull)){
      // 使用自定义云存储不限制
      if(bucketName.equals("oss-gxwebsoft")){
        return fail("存储空间已满", null);
      }
    }

    // 上传文件结果
    FileRecord result;
    CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
    // 创建OSSClient实例。
    // MinIO 兼容 S3 协议，需开启 PathStyle 访问（endpoint/bucket/key）并使用 V4 签名
    OSS ossClient;
    if ("minio".equals(uploadMethod)) {
      ClientBuilderConfiguration minioConfig = new ClientBuilderConfiguration();
      // 3.17 已移除 setPathStyleAccess；用 setExtractSettingFromEndpoint 让 SDK 对 MinIO 这类
      // 非阿里云 endpoint 自动按 path-style 访问，并从 endpoint 推导 region
      minioConfig.setSignatureVersion(SignVersion.V4);
      minioConfig.setExtractSettingFromEndpoint(true);
      ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider, minioConfig);
    } else {
      ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
    }

    try {

      String dir = getUploadDir();
      File upload = FileServerUtil.upload(file, dir, config.getUploadUuidName());
      String path = upload.getAbsolutePath().replace("\\", "/").substring(dir.length());
      String originalName = file.getOriginalFilename();

      // 创建PutObjectRequest对象。
      PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, path, upload);
      // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
      // ObjectMetadata metadata = new ObjectMetadata();
      // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
      // metadata.setObjectAcl(CannedAccessControlList.Private);
      // putObjectRequest.setMetadata(metadata);

      // 上传文件。
      PutObjectResult ossResult = ossClient.putObject(putObjectRequest);

      // 保存记录并返回
      result = new FileRecord();
      if(StrUtil.isNotBlank(authorization)){
        result.setCreateUserId(getLoginUserId());
      }
      if(StrUtil.isNotBlank(companyId)){
        result.setCompanyId(Integer.valueOf(companyId));
      }
      if(StrUtil.isNotBlank(merchantId)){
        result.setMerchantId(Long.valueOf(merchantId));
      }
      if(StrUtil.isNotBlank(groupId)){
        result.setGroupId(Integer.valueOf(groupId));
      }
      if(StrUtil.isNotBlank(appId)){
        result.setAppId(Integer.valueOf(appId));
      }
      path = "/".concat(path);
      result.setName(StrUtil.isBlank(originalName) ? upload.getName() : originalName);
      result.setLength(upload.length());
      result.setPath(bucketDomain + path);
      result.setDownloadUrl(bucketDomain + path);

      // 获取文件类型
      String contentType = FileServerUtil.getContentType(upload);
      result.setContentType(contentType);

      // 根据文件类型设置缩略图和预览图
      // MinIO 不支持阿里云 x-oss-process 图片处理参数，统一返回原图 URL
      boolean isMinio = "minio".equals(uploadMethod);
      if (FileServerUtil.isImage(contentType)) {
        if (isMinio) {
          result.setThumbnail(bucketDomain + path);
          result.setUrl(bucketDomain + path);
        } else {
          // 图片：生成缩略图和压缩预览图
          result.setThumbnail(bucketDomain + path + "?x-oss-process=image/resize,m_fixed,w_100,h_100/quality,Q_90");
          result.setUrl(bucketDomain + path + "?x-oss-process=image/resize,w_750/quality,Q_90");
        }
      } else if (contentType != null && contentType.startsWith("video/")) {
        if (isMinio) {
          result.setThumbnail(bucketDomain + path);
          result.setUrl(bucketDomain + path);
        } else {
          // 视频：使用OSS视频截帧生成缩略图（取首帧）
          result.setThumbnail(bucketDomain + path + "?x-oss-process=video/snapshot,t_0,f_jpg,w_100,h_100,m_fast");
          result.setUrl(bucketDomain + path);
        }
      } else {
        // 非图片/视频：直接使用原始URL
        result.setThumbnail(bucketDomain + path);
        result.setUrl(bucketDomain + path);
      }

      result.setTenantId(Integer.valueOf(tenantId));
      upload.delete();
      fileRecordService.save(result);
      // 更新存储空间
      if(companyId != null){
        Company company = companyService.getById(Integer.valueOf(companyId));
        company.setStorage(company.getStorage() + result.getLength());
        if(company.getStorage().compareTo(company.getStorageMax()) > 0){
          redisUtil.set(key,1);
        }
        companyService.updateById(company);
      }
      return success(result);

    } catch (OSSException oe) {
      System.out.println("Caught an OSSException, which means your request made it to OSS, "
        + "but was rejected with an error response for some reason.");
      System.out.println("Error Message:" + oe.getErrorMessage());
      System.out.println("Error Code:" + oe.getErrorCode());
      System.out.println("Request ID:" + oe.getRequestId());
      System.out.println("Host ID:" + oe.getHostId());
    } catch (ClientException ce) {
      System.out.println("Caught an ClientException, which means the client encountered "
        + "a serious internal problem while trying to communicate with OSS, "
        + "such as not being able to access the network.");
      System.out.println("Error Message:" + ce.getMessage());
    } finally {
      if (ossClient != null) {
        ossClient.shutdown();
      }
    }
    return fail("上传失败", null);
  }

  @OperationLog
  @Operation(summary = "获取临时osstoken")
  @GetMapping("/getSTSToken")
  public ApiResult<?> getSTSToken() {
    final User loginUser = getLoginUser();
    if(loginUser == null){
      return fail("请先登录");
    }
    // STS接入地址，例如sts.cn-hangzhou.aliyuncs.com。
    String endpoint = "sts.cn-shenzhen.aliyuncs.com";
    // 填写步骤1生成的RAM用户访问密钥AccessKey ID和AccessKey Secret。
    String accessKeyId = "LTAI5tBVtVkfKfJ5Fm6mzQzF";
    String accessKeySecret = "fsDI64rWJj9EXpqf7bNLn5YjigMUQe";
    // 填写步骤3获取的角色ARN。
    String roleArn = "acs:ram::1470199532233684:role/wsoss";
    // 自定义角色会话名称，用来区分不同的令牌，例如可填写为SessionTest。
    String roleSessionName = "wsoss";
    // 设置临时访问凭证的有效时间为3600秒。
    Long durationSeconds = 3600L;
    try {
      // regionId表示RAM的地域ID。以华东1（杭州）地域为例，regionID填写为cn-hangzhou。也可以保留默认值，默认值为空字符串（""）。
      String regionId = "";
      // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
      DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
      // 构造default profile。
      IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
      // 构造client。
      DefaultAcsClient client = new DefaultAcsClient(profile);
      final AssumeRoleRequest request = new AssumeRoleRequest();
      // 适用于Java SDK 3.12.0及以上版本。
      request.setSysMethod(MethodType.POST);
      // 适用于Java SDK 3.12.0以下版本。
      //request.setMethod(MethodType.POST);
      request.setRoleArn(roleArn);
      request.setRoleSessionName(roleSessionName);
//      request.setPolicy(policy);
      request.setDurationSeconds(durationSeconds);
      final AssumeRoleResponse response = client.getAcsResponse(request);
      return success(response);
    } catch (ClientException | com.aliyuncs.exceptions.ClientException e) {
      System.out.println("Failed：");
      System.out.println("Error message: " + e.getMessage());
      return fail(e.getMessage());

    }
  }

  /**
   * 获取前端表单提交的参数
   * @return
   */
  @Operation(summary = "获取前端表单提交的参数")
  @GetMapping("/getPostForm")
  public ApiResult<?> getPostForm(){
    String endpoint = config.getEndpoint();
    // RAM用户的访问密钥（AccessKey ID和AccessKey Secret）。
    String accessKeyId = "LTAI5t8UTh8CTXEi2dYxobhj";
    String accessKeySecret = "fNdJOT4KAjrVrzHNAcSJuUCy9ZljD9";
    // 使用代码嵌入的RAM用户的访问密钥配置访问凭证。
    CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
    // 填写Bucket名称，例如examplebucket。
    String bucket = config.getBucketName();
    OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);

    try {
      String host = "https://" + bucket + "." + endpoint;
      String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());


      long expireTime = 60;
      long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
      Date expiration = new Date(expireEndTime);
      PolicyConditions policyConds = new PolicyConditions();
      policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE,0,100*1024*1024);


      String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
      byte[]  binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
      String encodedPolicy = BinaryUtil.toBase64String(binaryData);
      String postSignature = ossClient.calculatePostSignature(postPolicy);
      Map result = new HashMap<>();
      result.put("polocyBase64",encodedPolicy);
      result.put("signature",postSignature);
      result.put("expireEndTime",expireEndTime);
      return success(result);
    } finally {
      ossClient.shutdown();
    }

  }

  /**
   * 文件上传位置(服务器)
   */
  private String getUploadDir() {
    return config.getUploadPath() + "/";
  }
}
