import type { InferResponseType } from 'hono/client';
import { fileClientManager, fileClient } from "../api/fileClient";

export interface MinioProgressEvent {
  stage: 'uploading' | 'complete' | 'error';
  message: string;
  progress: number;
  details?: {
      loaded: number;
      total: number;
  };
  timestamp: number;
}

export interface MinioProgressCallbacks {
  onProgress?: (event: MinioProgressEvent) => void;
  onComplete?: () => void;
  onError?: (error: Error) => void;
  signal?: AbortSignal;
}

export interface UploadResult {
  fileUrl:string;
  fileKey:string;
  bucketName:string;
}

interface UploadPart {
  ETag: string;
  PartNumber: number;
}

interface UploadProgressDetails {
  partNumber: number;
  totalParts: number;
  partSize: number;
  totalSize: number;
  partProgress?: number;
}

type MinioMultipartUploadPolicy = InferResponseType<typeof fileClient["multipart-policy"]['$post'],200>
type MinioUploadPolicy = InferResponseType<typeof fileClient["upload-policy"]['$post'],200>


const PART_SIZE = 5 * 1024 * 1024; // 每部分5MB


export class MinIOXHRMultipartUploader {
  /**
   * 使用XHR分段上传文件到MinIO
   */
  static async upload(
    policy: MinioMultipartUploadPolicy,
    file: File | Blob,
    key: string,
    callbacks?: MinioProgressCallbacks
  ): Promise<UploadResult> {
    const partSize = PART_SIZE;
    const totalSize = file.size;
    const totalParts = Math.ceil(totalSize / partSize);
    const uploadedParts: UploadPart[] = [];

    callbacks?.onProgress?.({
      stage: 'uploading',
      message: '准备上传文件...',
      progress: 0,
      details: {
        loaded: 0,
        total: totalSize
      },
      timestamp: Date.now()
    });

    // 分段上传
    for (let i = 0; i < totalParts; i++) {
      const start = i * partSize;
      const end = Math.min(start + partSize, totalSize);
      const partBlob = file.slice(start, end);
      const partNumber = i + 1;

      try {
        const etag = await this.uploadPart(
          policy.partUrls[i],
          partBlob,
          callbacks,
          {
            partNumber,
            totalParts,
            partSize: partBlob.size,
            totalSize
          }
        );

        uploadedParts.push({
          ETag: etag,
          PartNumber: partNumber
        });

        // 更新进度
        const progress = Math.round((end / totalSize) * 100);
        callbacks?.onProgress?.({
          stage: 'uploading',
          message: `上传文件片段 ${partNumber}/${totalParts}`,
          progress,
          details: {
            loaded: end,
            total: totalSize,
          },
          timestamp: Date.now()
        });
      } catch (error) {
        callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
        throw error;
      }
    }

    // 完成上传
    try {
      await this.completeMultipartUpload(policy, key, uploadedParts);

      callbacks?.onProgress?.({
        stage: 'complete',
        message: '文件上传完成',
        progress: 100,
        timestamp: Date.now()
      });

      callbacks?.onComplete?.();
      return {
        fileUrl: `${policy.host}/${key}`,
        fileKey: key,
        bucketName: policy.bucket
      };
    } catch (error) {
      callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  // 上传单个片段
  private static uploadPart(
    uploadUrl: string,
    partBlob: Blob,
    callbacks?: MinioProgressCallbacks,
    progressDetails?: UploadProgressDetails
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();

      xhr.upload.onprogress = (event) => {
        if (event.lengthComputable && callbacks?.onProgress) {
          const partProgress = Math.round((event.loaded / event.total) * 100);
          callbacks.onProgress({
            stage: 'uploading',
            message: `上传文件片段 ${progressDetails?.partNumber}/${progressDetails?.totalParts} (${partProgress}%)`,
            progress: Math.round((
              (progressDetails?.partNumber ? (progressDetails.partNumber - 1) * (progressDetails.partSize || 0) : 0) + event.loaded
            ) / (progressDetails?.totalSize || 1) * 100),
            details: {
              ...progressDetails,
              loaded: event.loaded,
              total: event.total
            },
            timestamp: Date.now()
          });
        }
      };

      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          // 获取ETag（MinIO返回的标识）
          const etag = xhr.getResponseHeader('ETag')?.replace(/"/g, '') || '';
          resolve(etag);
        } else {
          reject(new Error(`上传片段失败: ${xhr.status} ${xhr.statusText}`));
        }
      };

      xhr.onerror = () => reject(new Error('上传片段失败'));

      xhr.open('PUT', uploadUrl);
      xhr.send(partBlob);

      if (callbacks?.signal) {
        callbacks.signal.addEventListener('abort', () => {
          xhr.abort();
          reject(new Error('上传已取消'));
        });
      }
    });
  }

  // 完成分段上传
  private static async completeMultipartUpload(
    policy: MinioMultipartUploadPolicy,
    key: string,
    uploadedParts: UploadPart[]
  ): Promise<void> {
    const response = await fileClientManager.get()["multipart-complete"].$post({
        json:{
            bucket: policy.bucket,
            key,
            uploadId: policy.uploadId,
            parts: uploadedParts.map(part => ({ partNumber: part.PartNumber, etag: part.ETag }))
        }
    });

    if (!response.ok) {
      throw new Error(`完成分段上传失败: ${response.status} ${response.statusText}`);
    }
  }
}

export class MinIOXHRUploader {
    /**
     * 使用XHR上传文件到MinIO
     */
    static upload(
        policy: MinioUploadPolicy,
        file: File | Blob,
        key: string,
        callbacks?: MinioProgressCallbacks
    ): Promise<UploadResult> {
        const formData = new FormData();

        // 添加 MinIO 需要的字段
        Object.entries(policy.uploadPolicy).forEach(([k, value]) => {
            // 排除 policy 中的 key、host、prefix、ossType 字段
            if (k !== 'key' && k !== 'host' && k !== 'prefix' && k !== 'ossType' && typeof value === 'string') {
                formData.append(k, value);
            }
        });
        // 添加 自定义 key 字段
        formData.append('key', key);
        formData.append('file', file);

        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();

            // 上传进度处理
            if (callbacks?.onProgress) {
                xhr.upload.onprogress = (event) => {
                    if (event.lengthComputable) {
                        callbacks.onProgress?.({
                            stage: 'uploading',
                            message: '正在上传文件...',
                            progress: Math.round((event.loaded * 100) / event.total),
                            details: {
                                loaded: event.loaded,
                                total: event.total
                            },
                            timestamp: Date.now()
                        });
                    }
                };
            }

            // 完成处理
            xhr.onload = () => {
                if (xhr.status >= 200 && xhr.status < 300) {
                    if (callbacks?.onProgress) {
                        callbacks.onProgress({
                            stage: 'complete',
                            message: '文件上传完成',
                            progress: 100,
                            timestamp: Date.now()
                        });
                    }
                    callbacks?.onComplete?.();
                    resolve({
                        fileUrl:`${policy.uploadPolicy.host}/${key}`,
                        fileKey: key,
                        bucketName: policy.uploadPolicy.bucket
                    });
                } else {
                    const error = new Error(`上传失败: ${xhr.status} ${xhr.statusText}`);
                    callbacks?.onError?.(error);
                    reject(error);
                }
            };

            // 错误处理
            xhr.onerror = () => {
                const error = new Error('上传失败');
                if (callbacks?.onProgress) {
                    callbacks.onProgress({
                        stage: 'error',
                        message: '文件上传失败',
                        progress: 0,
                        timestamp: Date.now()
                    });
                }
                callbacks?.onError?.(error);
                reject(error);
            };

            // 根据当前页面协议和 host 配置决定最终的上传地址
            const currentProtocol = typeof window !== 'undefined' ? window.location.protocol : 'https:';
            const host = policy.uploadPolicy.host?.startsWith('http')
                ? policy.uploadPolicy.host
                : `${currentProtocol}//${policy.uploadPolicy.host}`;
            // 开始上传
            xhr.open('POST', host);
            xhr.send(formData);

            // 处理取消
            if (callbacks?.signal) {
                callbacks.signal.addEventListener('abort', () => {
                    xhr.abort();
                    reject(new Error('上传已取消'));
                });
            }
        });
    }
}

export async function getUploadPolicy(key: string, fileName: string, fileType?: string, fileSize?: number): Promise<MinioUploadPolicy> {
  const policyResponse = await fileClientManager.get()["upload-policy"].$post({
    json: {
      path: key,
      name: fileName,
      type: fileType,
      size: fileSize
    }
  });
  if (!policyResponse.ok) {
    throw new Error('获取上传策略失败');
  }
  return policyResponse.json();
}

export async function getMultipartUploadPolicy(totalSize: number, fileKey: string, fileType?: string, fileName: string = 'unnamed-file') {
  const policyResponse = await fileClientManager.get()["multipart-policy"].$post({
    json: {
      totalSize,
      partSize: PART_SIZE,
      fileKey,
      type: fileType,
      name: fileName
    }
  });
  if (!policyResponse.ok) {
    throw new Error('获取分段上传策略失败');
  }
  return await policyResponse.json();
}

export async function uploadMinIOWithPolicy(
  uploadPath: string,
  file: File | Blob,
  fileKey: string,
  callbacks?: MinioProgressCallbacks
): Promise<UploadResult> {
  if(uploadPath === '/') uploadPath = '';
  else{
    if(!uploadPath.endsWith('/')) uploadPath = `${uploadPath}/`
    // 去掉开头的 /
    if(uploadPath.startsWith('/')) uploadPath = uploadPath.replace(/^\//, '');
  }


  if( file.size > PART_SIZE ){
    if (!(file instanceof File)) {
      throw new Error('不支持的文件类型，无法获取文件名');
    }
    const policy = await getMultipartUploadPolicy(
      file.size,
      `${uploadPath}${fileKey}`,
      file.type,
      file.name
    );
    return MinIOXHRMultipartUploader.upload(
      policy,
      file,
      policy.key,
      callbacks
    );
  }else{
    if (!(file instanceof File)) {
      throw new Error('不支持的文件类型，无法获取文件名');
    }
    const policy = await getUploadPolicy(`${uploadPath}${fileKey}`, file.name, file.type, file.size);
    return MinIOXHRUploader.upload(policy, file, policy.uploadPolicy.key, callbacks);
  }
}