import {
  S3Client,
  PutObjectCommand,
  CreateMultipartUploadCommand,
  UploadPartCommand,
  CompleteMultipartUploadCommand,
} from '@aws-sdk/client-s3'
import {
  getOssSignatureUrl as apiGetOssSignatureUrl,
  checkOssSignatureUrlIsExist as apiCheckOssSignatureUrlIsExist,
  getSTSToken,
} from '@apis/oss'
import type { STSTokenResponse, UploadParams, UploadResult } from '../type'
import { OSSBucketType } from '../type'
import { StorageAdapter } from './base'

// 添加常量
const PART_SIZE = 5 * 1024 * 1024 // 5MB

export class MinIOAdapter extends StorageAdapter<S3Client> {
  private static instance: MinIOAdapter
  private config: Record<string, any> = {}

  // 获取默认实例键值
  protected getDefaultInstanceKey(): string {
    return 'minio-sts-token'
  }

  // 获取提供商前缀
  protected getProviderPrefix(): string {
    return 'custom-minio'
  }

  // 默认获取STS Token的方法
  protected async defaultGetSTSToken(): Promise<STSTokenResponse> {
    const token = await getSTSToken()
    return token
  }

  public static getInstance(): MinIOAdapter {
    if (!MinIOAdapter.instance) {
      MinIOAdapter.instance = new MinIOAdapter()
    }
    return MinIOAdapter.instance
  }

  // 添加创建自定义实例的静态方法
  public static createCustomInstance(
    getSTSTokenFn: () => Promise<STSTokenResponse>,
  ): MinIOAdapter {
    return new MinIOAdapter(getSTSTokenFn)
  }

  // 刷新S3客户端
  protected async refreshClient(): Promise<void> {
    try {
      // 获取最新的token
      const token = await this.getSTSToken()
      this.config = {
        ...token,
        endpoint: token.server_endpoint,
      }
      this.client = new S3Client({
        region: token.region,
        endpoint: token.server_endpoint,
        credentials: {
          accessKeyId: token.access_key_id,
          secretAccessKey: token.access_key_secret,
          ...(token.security_token
            ? { sessionToken: token.security_token }
            : {}),
        },
        forcePathStyle: true,
      })
      this.bucketMap = {
        [OSSBucketType.PRIVATE]: token.data_bucket!,
        [OSSBucketType.PUBLIC]: token.resource_bucket!,
      }
    } catch (error) {
      throw new Error(`MinIO客户端初始化失败: ${(error as Error).message}`)
    }
  }

  // 优化上传方法，提取公共逻辑
  private getUploadBucketAndUrl(params: UploadParams): {
    bucketName: string
    urlBase: string
  } {
    const bucketName =
      params.custom_bucket ||
      this.getBucketName(params.bucketType || OSSBucketType.PRIVATE)
    const urlBase = `${this.config.endpoint}/${bucketName}/${params.filePath}`
    return { bucketName, urlBase }
  }

  async upload(params: UploadParams): Promise<UploadResult> {
    try {
      const client = await this.getClient()
      const { bucketName, urlBase } = this.getUploadBucketAndUrl(params)
      const body = new Uint8Array(await params.file.arrayBuffer())
      const command = new PutObjectCommand({
        Bucket: bucketName,
        Key: params.filePath,
        Body: body,
        ContentType: params.file.type,
        ...params.options,
      })

      const result = await client.send(command)
      const url = urlBase

      return {
        url,
        result: {
          res: {
            requestUrls: [url],
            ...result,
          },
        },
      }
    } catch (error) {
      console.error('文件上传失败:', error)
      throw new Error(`文件上传失败: ${(error as Error).message}`)
    }
  }

  async multipartUpload(params: UploadParams): Promise<UploadResult> {
    try {
      const client = await this.getClient()
      const { bucketName, urlBase } = this.getUploadBucketAndUrl(params)
      const createCommand = new CreateMultipartUploadCommand({
        Bucket: bucketName,
        Key: params.filePath,
        ContentType: params.file.type,
        ...params.options,
      })

      const createResponse = await client.send(createCommand)
      const uploadId = createResponse.UploadId

      if (!uploadId) {
        throw new Error('初始化分片上传失败')
      }

      const fileBuffer = await params.file.arrayBuffer()
      const totalParts = Math.ceil(fileBuffer.byteLength / PART_SIZE)
      const uploadPromises = []

      for (let partNumber = 1; partNumber <= totalParts; partNumber++) {
        const start = (partNumber - 1) * PART_SIZE
        const end = Math.min(start + PART_SIZE, fileBuffer.byteLength)
        const partBuffer = fileBuffer.slice(start, end)

        const uploadPartCommand = new UploadPartCommand({
          Bucket: bucketName,
          Key: params.filePath,
          PartNumber: partNumber,
          UploadId: uploadId,
          Body: new Uint8Array(partBuffer),
        })

        uploadPromises.push(client.send(uploadPartCommand))
      }

      const uploadResults = await Promise.all(uploadPromises)

      const completeCommand = new CompleteMultipartUploadCommand({
        Bucket: bucketName,
        Key: params.filePath,
        UploadId: uploadId,
        MultipartUpload: {
          Parts: uploadResults.map((result, index) => ({
            ETag: result.ETag,
            PartNumber: index + 1,
          })),
        },
      })

      const completeResponse = await client.send(completeCommand)
      const url = urlBase

      return {
        url,
        result: {
          res: {
            requestUrls: [url],
            ...completeResponse,
          },
        },
      }
    } catch (error) {
      console.error('分片上传失败:', error)
      throw new Error(`分片上传失败: ${(error as Error).message}`)
    }
  }

  async getOssSignatureUrl(
    key: string,
    params?: Record<string, any>,
  ): Promise<string> {
    return apiGetOssSignatureUrl(key, params)
  }

  async checkOssSignatureUrlIsExist(): Promise<boolean> {
    return apiCheckOssSignatureUrlIsExist()
  }
}
