content: /*
   ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
   ~                                                                               ~
   ~ The MIT License (MIT)                                                         ~
   ~                                                                               ~
   ~ Copyright (c) 2015-2025 miaixz.org and other contributors.                    ~
   ~                                                                               ~
   ~ Permission is hereby granted, free of charge, to any person obtaining a copy  ~
   ~ of this software and associated documentation files (the "Software"), to deal ~
   ~ in the Software without restriction, including without limitation the rights  ~
   ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     ~
   ~ copies of the Software, and to permit persons to whom the Software is         ~
   ~ furnished to do so, subject to the following conditions:                      ~
   ~                                                                               ~
   ~ The above copyright notice and this permission notice shall be included in    ~
   ~ all copies or substantial portions of the Software.                           ~
   ~                                                                               ~
   ~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    ~
   ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      ~
   ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   ~
   ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        ~
   ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ~
   ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     ~
   ~ THE SOFTWARE.                                                                 ~
   ~                                                                               ~
   ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
  */
  package org.miaixz.bus.storage.metric;
  
  import java.io.*;
  import java.nio.file.Path;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.stream.Collectors;
  
  import org.miaixz.bus.core.basic.entity.Message;
  import org.miaixz.bus.core.lang.Assert;
  import org.miaixz.bus.core.lang.Normal;
  import org.miaixz.bus.core.xyz.IoKit;
  import org.miaixz.bus.core.xyz.StringKit;
  import org.miaixz.bus.gitlab.GitLabApi;
  import org.miaixz.bus.gitlab.models.RepositoryFile;
  import org.miaixz.bus.gitlab.models.TreeItem;
  import org.miaixz.bus.logger.Logger;
  import org.miaixz.bus.storage.Builder;
  import org.miaixz.bus.storage.Context;
  import org.miaixz.bus.storage.magic.ErrorCode;
  import org.miaixz.bus.storage.magic.Material;
  
  /**
   * 存储服务-Gitlab
   *
   * @author Kimi Liu
   * @since Java 17+
   */
  public class GitlabFileProvider extends AbstractProvider {
  
      private final GitLabApi client;
  
      /**
       * 使用给定的上下文构造 Gitlab 提供者。初始化 GitLab 客户端，使用提供的凭证和端点配置
       *
       * @param context 存储上下文，包含端点、存储桶（项目ID）、访问密钥等配置
       * @throws IllegalArgumentException 如果缺少或无效的必需上下文参数
       */
      public GitlabFileProvider(Context context) {
          this.context = context;
  
          Assert.notBlank(this.context.getEndpoint(), "[endpoint] cannot be blank");
          Assert.notBlank(this.context.getBucket(), "[bucket] cannot be blank");
          Assert.notBlank(this.context.getAccessKey(), "[accessKey] cannot be blank");
  
          this.client = new GitLabApi(this.context.getEndpoint(), this.context.getAccessKey());
      }
  
      /**
       * 从默认存储桶下载文件。
       *
       * @param fileName 文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message download(String fileName) {
          return download(this.context.getBucket(), fileName);
      }
  
      /**
       * 从指定存储桶下载文件。
       *
       * @param bucket   存储桶（项目ID）
       * @param fileName 文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message download(String bucket, String fileName) {
          try {
              String prefix = Builder.buildNormalizedPrefix(context.getPrefix());
              String objectKey = Builder.buildObjectKey(prefix, Normal.EMPTY, fileName);
              RepositoryFile file = client.getRepositoryFileApi().getFile(bucket, objectKey, "master");
              byte[] content = file.getContent().getBytes();
              BufferedReader bufferedReader = new BufferedReader(
                      new InputStreamReader(new ByteArrayInputStream(content)));
              return Message.builder().errcode(ErrorCode._SUCCESS.getKey()).errmsg(ErrorCode._SUCCESS.getValue())
                      .data(bufferedReader).build();
          } catch (Exception e) {
              Logger.error("Failed to download file: {} from bucket: {}. Error: {}", fileName, bucket, e.getMessage(), e);
              return Message.builder().errcode(ErrorCode._FAILURE.getKey()).errmsg(ErrorCode._FAILURE.getValue()).build();
          }
      }
  
      /**
       * 从默认存储桶下载文件并保存到本地文件。
       *
       * @param fileName 文件名
       * @param file     文件
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message download(String fileName, File file) {
          return download(this.context.getBucket(), fileName, file);
      }
  
      /**
       * 从指定存储桶下载文件并保存到本地文件。
       *
       * @param bucket   存储桶（项目ID）
       * @param fileName 文件名
       * @param file     文件
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message download(String bucket, String fileName, File file) {
          try {
              String prefix = Builder.buildNormalizedPrefix(context.getPrefix());
              String objectKey = Builder.buildObjectKey(prefix, Normal.EMPTY, fileName);
              RepositoryFile repositoryFile = client.getRepositoryFileApi().getFile(bucket, objectKey, "master");
              byte[] content = repositoryFile.getContent().getBytes();
              try (OutputStream outputStream = new FileOutputStream(file)) {
                  IoKit.copy(new ByteArrayInputStream(content), outputStream);
              }
              return Message.builder().errcode(ErrorCode._SUCCESS.getKey()).errmsg(ErrorCode._SUCCESS.getValue()).build();
          } catch (Exception e) {
              Logger.error(
                      "Failed to download file: {} from bucket: {} to local file: {}. Error: {}",
                      fileName,
                      bucket,
                      file.getAbsolutePath(),
                      e.getMessage(),
                      e);
              return Message.builder().errcode(ErrorCode._FAILURE.getKey()).errmsg("Failed to download file").build();
          }
      }
  
      /**
       * 列出默认存储桶中的文件。
       *
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message list() {
          try {
              String prefix = StringKit.isBlank(context.getPrefix()) ? null
                      : Builder.buildNormalizedPrefix(context.getPrefix()) + "/";
              // 使用 getTree 方法获取存储库树，分支为 "master"
              List<TreeItem> treeItems = client.getRepositoryApi()
                      .getTree(this.context.getBucket(), prefix, "master", true);
              return Message.builder().errcode(ErrorCode._SUCCESS.getKey()).errmsg(ErrorCode._SUCCESS.getValue())
                      .data(
                              treeItems.stream()
                                      // 过滤文件类型（假设 TreeItem 有 getType 方法，值为 "blob" 表示文件）
                                      .filter(item -> "blob".equals(item.getType())).map(item -> {
                                          Map<String, Object> extend = new HashMap<>();
                                          // 由于 getCommitId 不存在，暂时将 lastModified 设置为 null
                                          // 若需最后提交 ID，可通过 RepositoryFileApi 或 CommitsApi 获取
                                          extend.put("lastModified", null);
                                          return Material.builder().name(item.getPath()).size("0").extend(extend).build();
                                      }).collect(Collectors.toList()))
                      .build();
          } catch (Exception e) {
              Logger.error("Failed to list files in bucket: {}. Error: {}", this.context.getBucket(), e.getMessage(), e);
              return Message.builder().errcode(ErrorCode._FAILURE.getKey()).errmsg(ErrorCode._FAILURE.getValue()).build();
          }
      }
  
      /**
       * 重命名文件。
       *
       * @param oldName 原文件名
       * @param newName 新文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message rename(String oldName, String newName) {
          return rename(this.context.getBucket(), Normal.EMPTY, oldName, newName);
      }
  
      /**
       * 在默认存储桶中重命名文件。
       *
       * @param path    路径
       * @param oldName 原文件名
       * @param newName 新文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message rename(String path, String oldName, String newName) {
          return rename(this.context.getBucket(), path, oldName, newName);
      }
  
      /**
       * 在指定存储桶和路径中重命名文件。
       *
       * @param bucket  存储桶（项目ID）
       * @param path    路径
       * @param oldName 原文件名
       * @param newName 新文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message rename(String bucket, String path, String oldName, String newName) {
          try {
              String prefix = Builder.buildNormalizedPrefix(context.getPrefix());
              String oldObjectKey = Builder.buildObjectKey(prefix, path, oldName);
              String newObjectKey = Builder.buildObjectKey(prefix, path, newName);
              RepositoryFile file = client.getRepositoryFileApi().getFile(bucket, oldObjectKey, "master");
              if (file != null) {
                  client.getRepositoryFileApi().deleteFile(bucket, oldObjectKey, "master", "delete");
                  RepositoryFile newFile = new RepositoryFile();
                  newFile.setFilePath(newObjectKey);
                  newFile.setContent(file.getContent());
                  client.getRepositoryFileApi().createFile(bucket, newFile, "master", "create");
              }
              return Message.builder().errcode(ErrorCode._SUCCESS.getKey()).errmsg(ErrorCode._SUCCESS.getValue()).build();
          } catch (Exception e) {
              Logger.error(
                      "Failed to rename file: from {} to {} in bucket: {} path: {}. Error: {}",
                      oldName,
                      newName,
                      bucket,
                      path,
                      e.getMessage(),
                      e);
              return Message.builder().errcode(ErrorCode._FAILURE.getKey()).errmsg(ErrorCode._FAILURE.getValue()).build();
          }
      }
  
      /**
       * 上传字节数组内容到默认存储桶。
       *
       * @param fileName 文件名
       * @param content  字节数组
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message upload(String fileName, byte[] content) {
          return upload(this.context.getBucket(), Normal.EMPTY, fileName, content);
      }
  
      /**
       * 上传字节数组内容到指定存储桶。
       *
       * @param path     路径
       * @param fileName 文件名
       * @param content  字节数组
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message upload(String path, String fileName, byte[] content) {
          return upload(this.context.getBucket(), path, fileName, content);
      }
  
      /**
       * 上传字节数组内容到指定存储桶和路径。
       *
       * @param bucket   存储桶（项目ID）
       * @param path     路径
       * @param fileName 文件名
       * @param content  字节数组
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message upload(String bucket, String path, String fileName, byte[] content) {
          return upload(bucket, path, fileName, new ByteArrayInputStream(content));
      }
  
      /**
       * 上传输入流内容到默认存储桶。
       *
       * @param fileName 文件名
       * @param content  输入流
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message upload(String fileName, InputStream content) {
          return upload(this.context.getBucket(), Normal.EMPTY, fileName, content);
      }
  
      /**
       * 上传输入流内容到默认存储桶指定路径。
       *
       * @param path     路径
       * @param fileName 文件名
       * @param content  输入流
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message upload(String path, String fileName, InputStream content) {
          return upload(this.context.getBucket(), path, fileName, content);
      }
  
      /**
       * 上传输入流内容到指定存储桶和路径。
       *
       * @param bucket   存储桶（项目ID）
       * @param path     路径
       * @param fileName 文件名
       * @param content  输入流
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message upload(String bucket, String path, String fileName, InputStream content) {
          try {
              String prefix = Builder.buildNormalizedPrefix(context.getPrefix());
              String objectKey = Builder.buildObjectKey(prefix, path, fileName);
              byte[] contentBytes = IoKit.readBytes(content);
              RepositoryFile file = new RepositoryFile();
              file.setFilePath(objectKey);
              file.setContent(new String(contentBytes));
              client.getRepositoryFileApi().createFile(bucket, file, "master", "upload");
              String url = client.getProjectApi().getProject(bucket).getWebUrl() + "/-/blob/master/" + objectKey;
              return Message.builder().errcode(ErrorCode._SUCCESS.getKey()).errmsg(ErrorCode._SUCCESS.getValue())
                      .data(Material.builder().name(fileName).url(url).path(objectKey).build()).build();
          } catch (Exception e) {
              Logger.error(
                      "Failed to upload file: {} to bucket: {} path: {}. Error: {}",
                      fileName,
                      bucket,
                      path,
                      e.getMessage(),
                      e);
              return Message.builder().errcode(ErrorCode._FAILURE.getKey()).errmsg(ErrorCode._FAILURE.getValue()).build();
          }
      }
  
      /**
       * 从默认存储桶删除文件。
       *
       * @param fileName 文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message remove(String fileName) {
          return remove(this.context.getBucket(), Normal.EMPTY, fileName);
      }
  
      /**
       * 从指定存储桶删除文件。
       *
       * @param path     路径
       * @param fileName 文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message remove(String path, String fileName) {
          return remove(path, Normal.EMPTY, fileName);
      }
  
      /**
       * 从指定存储桶和路径删除文件。
       *
       * @param bucket   存储桶（项目ID）
       * @param path     路径
       * @param fileName 文件名
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message remove(String bucket, String path, String fileName) {
          try {
              String prefix = Builder.buildNormalizedPrefix(context.getPrefix());
              String objectKey = Builder.buildObjectKey(prefix, path, fileName);
              client.getRepositoryFileApi().deleteFile(bucket, objectKey, "master", "delete");
              return Message.builder().errcode(ErrorCode._SUCCESS.getKey()).errmsg(ErrorCode._SUCCESS.getValue()).build();
          } catch (Exception e) {
              Logger.error(
                      "Failed to delete file: {} from bucket: {} path: {}. Error: {}",
                      fileName,
                      bucket,
                      path,
                      e.getMessage(),
                      e);
              return Message.builder().errcode(ErrorCode._FAILURE.getKey()).errmsg(ErrorCode._FAILURE.getValue()).build();
          }
      }
  
      /**
       * 从指定存储桶删除文件（基于路径）。
       *
       * @param bucket 存储桶（项目ID）
       * @param path   要删除的文件路径
       * @return 处理结果 {@link Message}
       */
      @Override
      public Message remove(String bucket, Path path) {
          return remove(bucket, path.toString(), Normal.EMPTY);
      }
  
  }
  