import * as Minio from 'minio';

// MinIO 客户端配置
const useSSL = process.env.MINIO_USE_SSL === 'true';
const port = Number(process.env.MINIO_PORT) || 9000;

// 当使用标准HTTPS端口（443）或HTTP端口（80）时，MinIO客户端不需要指定端口
const shouldOmitPort = (useSSL && port === 443) || (!useSSL && port === 80);

const minioClient = new Minio.Client({
  endPoint: process.env.MINIO_ENDPOINT || 'localhost',
  ...(shouldOmitPort ? {} : { port }),
  useSSL,
  accessKey: process.env.MINIO_ACCESS_KEY || '',
  secretKey: process.env.MINIO_SECRET_KEY || '',
  pathStyle: true, // 使用路径风格，对反向代理更友好
});

const BUCKET_NAME = process.env.MINIO_BUCKET_NAME || 'home-page';

/**
 * 初始化 MinIO Bucket（确保 bucket 存在）
 */
export async function initBucket(): Promise<void> {
  try {
    const exists = await minioClient.bucketExists(BUCKET_NAME);
    if (!exists) {
      await minioClient.makeBucket(BUCKET_NAME, 'us-east-1');
      console.log(`Bucket ${BUCKET_NAME} created successfully`);
    }
    // 设置公共读取策略（可选）
    const policy = {
      Version: '2012-10-17',
      Statement: [
        {
          Effect: 'Allow',
          Principal: { AWS: ['*'] },
          Action: ['s3:GetObject'],
          Resource: [`arn:aws:s3:::${BUCKET_NAME}/*`],
        },
      ],
    };
    await minioClient.setBucketPolicy(BUCKET_NAME, JSON.stringify(policy));
  } catch (error) {
    console.error('Error initializing bucket:', error);
    throw error;
  }
}



/**
 * 上传文件到 MinIO
 * @param file - File 对象或 Buffer
 * @param path - 文件存储路径（如: 'avatars/user123.jpg' 或 'posts/2024/image.png'）
 * @param metadata - 可选的元数据
 * @returns 文件 path = args.path 
 */
export async function uploadFile(
  file: File | Buffer,
  path: string,
  metadata?: Record<string, string>
): Promise<string> {
  try {
    await initBucket();

    let buffer: Buffer;
    let contentType: string;

    if (file instanceof File) {
      buffer = Buffer.from(await file.arrayBuffer());
      contentType = file.type || 'application/octet-stream';
    } else {
      buffer = file;
      contentType = metadata?.['Content-Type'] || 'application/octet-stream';
    }

    const metaData = {
      'Content-Type': contentType,
      ...metadata,
    };

    await minioClient.putObject(BUCKET_NAME, path, buffer, buffer.length, metaData);

    return path;
  } catch (error) {
    console.error('Error uploading file:', error);
    throw error;
  }
}


/**
 * 获取文件的公共访问URL
 * @param path - 文件路径
 * @returns 文件的访问URL
 */
export function getFileUrl(path: string): string {
  const endpoint = process.env.MINIO_PUBLIC_DOMAIN;

  return `${endpoint}/${BUCKET_NAME}/${path}`;
}

/**
 * 下载文件
 * @param path - 文件路径
 * @returns 文件的 Buffer
 */
export async function downloadFile(path: string): Promise<Buffer> {
  try {
    const chunks: Buffer[] = [];
    const stream = await minioClient.getObject(BUCKET_NAME, path);

    return new Promise((resolve, reject) => {
      stream.on('data', (chunk) => chunks.push(chunk));
      stream.on('end', () => resolve(Buffer.concat(chunks)));
      stream.on('error', reject);
    });
  } catch (error) {
    console.error('Error downloading file:', error);
    throw error;
  }
}

/**
 * 获取文件流
 * @param path - 文件路径
 * @returns 文件流
 */
export async function getFileStream(path: string): Promise<NodeJS.ReadableStream> {
  try {
    return await minioClient.getObject(BUCKET_NAME, path);
  } catch (error) {
    console.error('Error getting file stream:', error);
    throw error;
  }
}

/**
 * 删除文件
 * @param path - 文件路径
 */
export async function deleteFile(path: string): Promise<void> {
  try {
    await minioClient.removeObject(BUCKET_NAME, path);
  } catch (error) {
    console.error('Error deleting file:', error);
    throw error;
  }
}

/**
 * 批量删除文件
 * @param paths - 文件路径数组
 */
export async function deleteFiles(paths: string[]): Promise<void> {
  try {
    await minioClient.removeObjects(BUCKET_NAME, paths);
  } catch (error) {
    console.error('Error deleting files:', error);
    throw error;
  }
}

/**
 * 检查文件是否存在
 * @param path - 文件路径
 * @returns 文件是否存在
 */
export async function fileExists(path: string): Promise<boolean> {
  try {
    await minioClient.statObject(BUCKET_NAME, path);
    return true;
  } catch (error) {
    return false;
  }
}

/**
 * 获取文件信息
 * @param path - 文件路径
 * @returns 文件元数据
 */
export async function getFileInfo(path: string): Promise<Minio.BucketItemStat> {
  try {
    return await minioClient.statObject(BUCKET_NAME, path);
  } catch (error) {
    console.error('Error getting file info:', error);
    throw error;
  }
}

/**
 * 列出指定路径下的所有文件
 * @param prefix - 路径前缀（如: 'avatars/' 或 'posts/2024/'）
 * @param recursive - 是否递归列出子目录
 * @returns 文件列表
 */
export async function listFiles(
  prefix: string = '',
  recursive: boolean = false
): Promise<(Minio.BucketItem & { endpoint: string })[]> {
  try {
    const files: (Minio.BucketItem & { endpoint: string })[] = [];
    const stream = minioClient.listObjects(BUCKET_NAME, prefix, recursive);

    return new Promise((resolve, reject) => {
      stream.on('data', (obj) => {
        if (obj.name) {
          files.push({ endpoint: `${process.env.MINIO_PUBLIC_DOMAIN}/${BUCKET_NAME}`, ...obj, } as Minio.BucketItem & { endpoint: string });
        }
      });
      stream.on('end', () => resolve(files));
      stream.on('error', reject);
    });
  } catch (error) {
    console.error('Error listing files:', error);
    throw error;
  }
}

/**
 * 复制文件
 * @param sourcePath - 源文件路径
 * @param destPath - 目标文件路径
 */
export async function copyFile(sourcePath: string, destPath: string): Promise<void> {
  try {
    const conds = new Minio.CopyConditions();
    await minioClient.copyObject(
      BUCKET_NAME,
      destPath,
      `/${BUCKET_NAME}/${sourcePath}`,
      conds
    );
  } catch (error) {
    console.error('Error copying file:', error);
    throw error;
  }
}

/**
 * 生成唯一文件名
 * @param originalName - 原始文件名
 * @param prefix - 路径前缀（如: 'avatars/' 或 'posts/2024/'）
 * @returns 带路径的唯一文件名
 */
export function generateUniqueFileName(originalName: string, prefix: string = ''): string {
  const timestamp = Date.now();
  const random = Math.random().toString(36).substring(2, 8);
  const ext = originalName.split('.').pop();
  const nameWithoutExt = originalName.replace(`.${ext}`, '').replace(/[^a-zA-Z0-9]/g, '_');

  const fileName = `${nameWithoutExt}_${timestamp}_${random}.${ext}`;

  return prefix ? `${prefix.replace(/\/$/, '')}/${fileName}` : fileName;
}

/**
 * 获取文件扩展名
 * @param filename - 文件名
 * @returns 扩展名（不含点）
 */
export function getFileExtension(filename: string): string {
  return filename.split('.').pop() || '';
}

/**
 * 验证文件类型
 * @param filename - 文件名
 * @param allowedTypes - 允许的扩展名数组（如: ['jpg', 'png', 'gif']）
 * @returns 是否为允许的类型
 */
export function validateFileType(filename: string, allowedTypes: string[]): boolean {
  const ext = getFileExtension(filename).toLowerCase();
  return allowedTypes.map(t => t.toLowerCase()).includes(ext);
}

/**
 * 验证文件大小
 * @param size - 文件大小（字节）
 * @param maxSize - 最大大小（字节）
 * @returns 是否在允许的大小范围内
 */
export function validateFileSize(size: number, maxSize: number): boolean {
  return size <= maxSize;
}

/**
 * 格式化文件大小
 * @param bytes - 字节数
 * @returns 格式化后的文件大小
 */
export function formatFileSize(bytes: number): string {
  if (bytes === 0) return '0 B';

  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));

  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}

export default {
  initBucket,
  uploadFile,
  getFileUrl,
  downloadFile,
  getFileStream,
  deleteFile,
  deleteFiles,
  fileExists,
  getFileInfo,
  listFiles,
  copyFile,
  generateUniqueFileName,
  getFileExtension,
  validateFileType,
  validateFileSize,
  formatFileSize,
};
