import type { InferResponseType } from 'hono/client';
import { fileClient } from "../api";
import { isWeapp, isH5 } from './platform';
import Taro from '@tarojs/taro';

// 平台检测 - 使用统一的 platform.ts
const isMiniProgram = isWeapp();
const isBrowser = isH5();

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 | { aborted: boolean };
}

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

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

// ==================== H5 实现（保留原有代码） ====================
export class MinIOXHRMultipartUploader {
  /**
   * 使用XHR分段上传文件到MinIO（H5环境）
   */
  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 {
      const result = 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,
        fileId: result.fileId
      };
    } 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) {
          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) {
        if ('addEventListener' in callbacks.signal) {
          callbacks.signal.addEventListener('abort', () => {
            xhr.abort();
            reject(new Error('上传已取消'));
          });
        }
      }
    });
  }
  
  // 完成分段上传
  private static async completeMultipartUpload(
    policy: MinioMultipartUploadPolicy,
    key: string,
    uploadedParts: UploadPart[]
  ): Promise<{ fileId: number }> {
    const response = await fileClient["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}`);
    }
    
    return response.json();
  }
}

export class MinIOXHRUploader {
  /**
   * 使用XHR上传文件到MinIO（H5环境）
   */
  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]) => {
      if (k !== 'key' && k !== 'host' && k !== 'prefix' && k !== 'ossType' && typeof value === 'string') {
        formData.append(k, value);
      }
    });
    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,
            fileId: policy.file.id
          });
        } 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) {
        if ('addEventListener' in callbacks.signal) {
          callbacks.signal.addEventListener('abort', () => {
            xhr.abort();
            reject(new Error('上传已取消'));
          });
        }
      }
    });
  }
}

// ==================== 小程序实现 ====================
export class TaroMinIOMultipartUploader {
  /**
   * 使用 Taro 分段上传文件到 MinIO（小程序环境）
   */
  static async upload(
    policy: MinioMultipartUploadPolicy,
    filePath: string,
    key: string,
    callbacks?: MinioProgressCallbacks
  ): Promise<UploadResult> {
    const partSize = PART_SIZE;
    
    // 获取文件信息
    const fileInfo = await getFileInfoPromise(filePath);
    const totalSize = fileInfo.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++) {
      if (callbacks?.signal && 'aborted' in callbacks.signal && callbacks.signal.aborted) {
        throw new Error('上传已取消');
      }
      
      const start = i * partSize;
      const end = Math.min(start + partSize, totalSize);
      const partNumber = i + 1;
      
      try {
        // 读取文件片段
        const partData = await this.readFileSlice(filePath, start, end);
        
        const etag = await this.uploadPart(
          policy.partUrls[i],
          partData,
          callbacks,
          {
            partNumber,
            totalParts,
            partSize: end - start,
            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 {
      const result = 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,
        fileId: result.fileId
      };
    } catch (error) {
      callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }
  
  // 读取文件片段
  private static async readFileSlice(filePath: string, start: number, end: number): Promise<ArrayBuffer> {
    return new Promise((resolve, reject) => {
      try {
        const fs = Taro?.getFileSystemManager?.();
        if (!fs) {
          reject(new Error('小程序文件系统不可用'));
          return;
        }
        
        const fileData = fs.readFileSync(filePath, undefined, start, end - start);
        
        // 确保返回 ArrayBuffer 类型
        if (typeof fileData === 'string') {
          // 将字符串转换为 ArrayBuffer
          const encoder = new TextEncoder();
          resolve(encoder.encode(fileData).buffer);
        } else if (fileData instanceof ArrayBuffer) {
          resolve(fileData);
        } else {
          // 处理其他可能的数据类型
          reject(new Error('文件数据类型不支持'));
        }
      } catch (error) {
        reject(error);
      }
    });
  }
  
  // 上传单个片段
  private static async uploadPart(
    uploadUrl: string,
    partData: ArrayBuffer,
    callbacks?: MinioProgressCallbacks,
    progressDetails?: UploadProgressDetails
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      Taro?.request?.({
        url: uploadUrl,
        method: 'PUT',
        data: partData,
        header: {
          'Content-Type': 'application/octet-stream'
        },
        success: (res: any) => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            const etag = res.header?.['ETag']?.replace(/"/g, '') || '';
            resolve(etag);
          } else {
            reject(new Error(`上传片段失败: ${res.statusCode}`));
          }
        },
        fail: (error: any) => {
          reject(new Error(`上传片段失败: ${error.errMsg}`));
        }
      }) || reject(new Error('小程序环境不可用'));
    });
  }
  
  // 完成分段上传
  private static async completeMultipartUpload(
    policy: MinioMultipartUploadPolicy,
    key: string,
    uploadedParts: UploadPart[]
  ): Promise<{ fileId: number }> {
    const response = await fileClient["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}`);
    }
    
    return await response.json();
  }
}

export class TaroMinIOUploader {
  /**
   * 使用 Taro 上传文件到 MinIO（小程序环境）
   */
  static async upload(
    policy: MinioUploadPolicy,
    filePath: string,
    key: string,
    callbacks?: MinioProgressCallbacks
  ): Promise<UploadResult> {
    // 获取文件信息
    const fileInfo = await getFileInfoPromise(filePath);
    const totalSize = fileInfo.size;
    
    callbacks?.onProgress?.({
      stage: 'uploading',
      message: '准备上传文件...',
      progress: 0,
      details: {
        loaded: 0,
        total: totalSize
      },
      timestamp: Date.now()
    });
    
    return new Promise((resolve, reject) => {
      // 准备表单数据 - 使用对象形式，Taro.uploadFile会自动处理
      const formData: Record<string, string> = {};
      
      // 添加 MinIO 需要的字段
      Object.entries(policy.uploadPolicy).forEach(([k, value]) => {
        if (k !== 'key' && k !== 'host' && k !== 'prefix' && k !== 'ossType' && typeof value === 'string') {
          formData[k] = value;
        }
      });
      
      formData['key'] = key;
      
      // 使用 Taro.uploadFile 替代 FormData
      const uploadTask = Taro.uploadFile({
        url: policy.uploadPolicy.host,
        filePath: filePath,
        name: 'file',
        formData: formData,
        header: {
          'Content-Type': 'multipart/form-data'
        },
        success: (res) => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            callbacks?.onProgress?.({
              stage: 'complete',
              message: '文件上传完成',
              progress: 100,
              timestamp: Date.now()
            });
            callbacks?.onComplete?.();
            resolve({
              fileUrl: `${policy.uploadPolicy.host}/${key}`,
              fileKey: key,
              bucketName: policy.uploadPolicy.bucket,
              fileId: policy.file.id
            });
          } else {
            reject(new Error(`上传失败: ${res.statusCode}`));
          }
        },
        fail: (error) => {
          reject(new Error(`上传失败: ${error.errMsg}`));
        }
      });

      // 监听上传进度
      uploadTask.progress((res) => {
        if (res.totalBytesExpectedToSend > 0) {
          const currentProgress = Math.round((res.totalBytesSent / res.totalBytesExpectedToSend) * 100);
          callbacks?.onProgress?.({
            stage: 'uploading',
            message: `上传中 ${currentProgress}%`,
            progress: currentProgress,
            details: {
              loaded: res.totalBytesSent,
              total: res.totalBytesExpectedToSend
            },
            timestamp: Date.now()
          });
        }
      });

      // 支持取消上传
      if (callbacks?.signal && 'aborted' in callbacks.signal) {
        if (callbacks.signal.aborted) {
          uploadTask.abort();
          reject(new Error('上传已取消'));
        }
        
        // 监听取消信号
        const checkAbort = () => {
          if (callbacks.signal?.aborted) {
            uploadTask.abort();
            reject(new Error('上传已取消'));
          }
        };
        
        // 定期检查取消状态
        const abortInterval = setInterval(checkAbort, 100);
        
        // 清理定时器
        const cleanup = () => clearInterval(abortInterval);
        uploadTask.onProgressUpdate = cleanup;
        uploadTask.onHeadersReceived = cleanup;
      }
    });
  }
}

// ==================== 统一 API ====================
/**
 * 根据运行环境自动选择合适的上传器
 */
export class UniversalMinIOMultipartUploader {
  static async upload(
    policy: MinioMultipartUploadPolicy,
    file: File | Blob | string,
    key: string,
    callbacks?: MinioProgressCallbacks
  ): Promise<UploadResult> {
    if (isBrowser && (file instanceof File || file instanceof Blob)) {
      return MinIOXHRMultipartUploader.upload(policy, file, key, callbacks);
    } else if (isMiniProgram && typeof file === 'string') {
      return TaroMinIOMultipartUploader.upload(policy, file, key, callbacks);
    } else {
      throw new Error('不支持的运行环境或文件类型');
    }
  }
}

export class UniversalMinIOUploader {
  static async upload(
    policy: MinioUploadPolicy,
    file: File | Blob | string,
    key: string,
    callbacks?: MinioProgressCallbacks
  ): Promise<UploadResult> {
    if (isBrowser && (file instanceof File || file instanceof Blob)) {
      return MinIOXHRUploader.upload(policy, file, key, callbacks);
    } else if (isMiniProgram && typeof file === 'string') {
      return TaroMinIOUploader.upload(policy, file, key, callbacks);
    } else {
      throw new Error('不支持的运行环境或文件类型');
    }
  }
}

// ==================== 通用函数 ====================
export async function getUploadPolicy(key: string, fileName: string, fileType?: string, fileSize?: number): Promise<MinioUploadPolicy> {
  const policyResponse = await fileClient["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 fileClient["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 | string,
  fileKey: string,
  callbacks?: MinioProgressCallbacks
): Promise<UploadResult> {
  if(uploadPath === '/') uploadPath = '';
  else{
    if(!uploadPath.endsWith('/')) uploadPath = `${uploadPath}/`
    if(uploadPath.startsWith('/')) uploadPath = uploadPath.replace(/^\//, '');
  }
  
  let fileSize: number;
  let fileType: string | undefined;
  let fileName: string;
  
  if (isBrowser && (file instanceof File || file instanceof Blob)) {
    fileSize = file.size;
    fileType = (file as File).type || undefined;
    fileName = (file as File).name || fileKey;
  } else if (isMiniProgram && typeof file === 'string') {
    try {
      const fileInfo = await getFileInfoPromise(file);
      fileSize = fileInfo.size;
      fileType = undefined;
      fileName = fileKey;
    } catch {
      fileSize = 0;
      fileType = undefined;
      fileName = fileKey;
    }
  } else {
    throw new Error('不支持的文件类型');
  }
  
  if (fileSize > PART_SIZE) {
    if (isBrowser && !(file instanceof File)) {
      throw new Error('不支持的文件类型，无法获取文件名');
    }
    
    const policy = await getMultipartUploadPolicy(
      fileSize,
      `${uploadPath}${fileKey}`,
      fileType,
      fileName
    );
    
    if (isBrowser) {
      return MinIOXHRMultipartUploader.upload(policy, file as File | Blob, policy.key, callbacks);
    } else {
      return TaroMinIOMultipartUploader.upload(policy, file as string, policy.key, callbacks);
    }
  } else {
    if (isBrowser && !(file instanceof File)) {
      throw new Error('不支持的文件类型，无法获取文件名');
    }
    
    const policy = await getUploadPolicy(`${uploadPath}${fileKey}`, fileName, fileType, fileSize);
    
    if (isBrowser) {
      return MinIOXHRUploader.upload(policy, file as File | Blob, policy.uploadPolicy.key, callbacks);
    } else {
      return TaroMinIOUploader.upload(policy, file as string, policy.uploadPolicy.key, callbacks);
    }
  }
}

// ==================== 小程序工具函数 ====================
/**
 * Promise封装的getFileInfo函数
 */
async function getFileInfoPromise(filePath: string): Promise<{ size: number }> {
  return new Promise((resolve, reject) => {
    const fs = Taro?.getFileSystemManager?.();
    if (!fs) {
      reject(new Error('小程序文件系统不可用'));
      return;
    }
    
    fs.getFileInfo({
      filePath,
      success: (res) => {
        resolve({ size: res.size });
      },
      fail: (error) => {
        reject(new Error(`获取文件信息失败: ${error.errMsg}`));
      }
    });
  });
}

// 新增：自动适应运行环境的文件选择并上传函数
/**
 * 自动适应运行环境：选择文件并上传到 MinIO
 * 小程序：使用 Taro.chooseImage
 * H5：使用 input[type="file"]
 */
export async function uploadFromSelect(
  uploadPath: string = '',
  options: {
    sourceType?: ('album' | 'camera')[],
    count?: number,
    accept?: string,
    maxSize?: number,
  } = {},
  callbacks?: MinioProgressCallbacks
): Promise<UploadResult> {
  const { sourceType = ['album', 'camera'], count = 1, accept = '*', maxSize = 10 * 1024 * 1024 } = options;

  if (isMiniProgram) {
    return new Promise((resolve, reject) => {
      
      Taro.chooseImage({
        count,
        sourceType: sourceType as any, // 确保类型兼容
        success: async (res) => {
          const tempFilePath = res.tempFilePaths[0];
          const fileName = res.tempFiles[0]?.originalFileObj?.name || tempFilePath.split('/').pop() || 'unnamed-file';
          
          try {
            const result = await uploadMinIOWithPolicy(uploadPath, tempFilePath, fileName, callbacks);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        },
        fail: reject
      });
    });
  } else if (isBrowser) {
    return new Promise((resolve, reject) => {
      const input = document.createElement('input');
      input.type = 'file';
      input.accept = accept;
      input.multiple = count > 1;
      
      input.onchange = async (event) => {
        const files = (event.target as HTMLInputElement).files;
        if (!files || files.length === 0) {
          reject(new Error('未选择文件'));
          return;
        }
        
        const file = files[0];
        if (file.size > maxSize) {
          reject(new Error(`文件大小超过限制: ${maxSize / 1024 / 1024}MB`));
          return;
        }
        
        const fileName = file.name || 'unnamed-file';
        
        try {
          const result = await uploadMinIOWithPolicy(uploadPath, file, fileName, callbacks);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      };
      
      input.click();
    });
  } else {
    throw new Error('不支持的运行环境');
  }
}

// 默认导出
export default {
  MinIOXHRMultipartUploader,
  MinIOXHRUploader,
  TaroMinIOMultipartUploader,
  TaroMinIOUploader,
  UniversalMinIOMultipartUploader,
  UniversalMinIOUploader,
  getUploadPolicy,
  getMultipartUploadPolicy,
  uploadMinIOWithPolicy,
  uploadFromSelect
};