#!/usr/bin/env node
/**
 * S3 文件上传脚本
 * 用法：node upload-s3.mjs <文件路径> [S3目录前缀]
 *
 * 示例：
 *   node upload-s3.mjs ./test.png              → https://ibitcex.s3.ap-southeast-1.amazonaws.com/test.png
 *   node upload-s3.mjs ./logo.png images       → https://ibitcex.s3.ap-southeast-1.amazonaws.com/images/logo.png
 */

import { readFileSync, existsSync } from 'fs';
import { basename, resolve } from 'path';
import { fileURLToPath } from 'url';
import { createHash, createHmac } from 'crypto';
import { request } from 'https';

// ========== S3 配置 ==========
const CONFIG = {
  accessKeyId: 'AKIARHQKVVUBD3M6E3XV',
  secretAccessKey: '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',
  bucket: 'ibitcex',
  region: 'ap-southeast-1',
};

// ========== AWS Signature V4 签名 ==========
const SERVICE = 's3';
const ALGORITHM = 'AWS4-HMAC-SHA256';

function hmac(key, data) {
  return createHmac('sha256', key).update(data).digest();
}

function sha256(data) {
  return createHash('sha256').update(data).digest('hex');
}

function getSigningKey(dateStamp) {
  const kDate = hmac('AWS4' + CONFIG.secretAccessKey, dateStamp);
  const kRegion = hmac(kDate, CONFIG.region);
  const kService = hmac(kRegion, SERVICE);
  return hmac(kService, 'aws4_request');
}

function getSignature(key, stringToSign) {
  return hmac(key, stringToSign).toString('hex');
}

// ========== 上传 ==========
function uploadFile(filePath, s3Key) {
  const fileContent = readFileSync(filePath);
  const host = `${CONFIG.bucket}.s3.${CONFIG.region}.amazonaws.com`;

  const amzDate = new Date().toISOString().replace(/[:-]|\.\d{3}/g, '');
  const dateStamp = amzDate.substring(0, 8);

  const payloadHash = sha256(fileContent);

  const headers = {
    'Host': host,
    'Content-Length': String(fileContent.length),
    'Content-Type': 'application/octet-stream',
    'x-amz-content-sha256': payloadHash,
    'x-amz-date': amzDate,
  };

  const signedHeaders = Object.keys(headers)
    .sort()
    .map(k => k.toLowerCase())
    .join(';');

  // Canonical Request
  const canonicalRequest = [
    'PUT',
    '/' + encodeURIComponent(s3Key).replace(/%2F/g, '/'),
    '',
    ...Object.keys(headers).sort().map(k => `${k.toLowerCase()}:${headers[k].trim()}`),
    '',
    signedHeaders,
    payloadHash,
  ].join('\n');

  // String to Sign
  const stringToSign = [
    ALGORITHM,
    amzDate,
    `${dateStamp}/${CONFIG.region}/${SERVICE}/aws4_request`,
    sha256(canonicalRequest),
  ].join('\n');

  const signingKey = getSigningKey(dateStamp);
  const signature = getSignature(signingKey, stringToSign);

  const authorization = `${ALGORITHM} Credential=${CONFIG.accessKeyId}/${dateStamp}/${CONFIG.region}/${SERVICE}/aws4_request,SignedHeaders=${signedHeaders},Signature=${signature}`;

  return new Promise((resolvePromise, reject) => {
    const req = request(
      {
        hostname: host,
        path: '/' + s3Key,
        method: 'PUT',
        headers: { ...headers, Authorization: authorization },
      },
      (res) => {
        let body = '';
        res.on('data', (chunk) => (body += chunk));
        res.on('end', () => {
          if (res.statusCode === 200) {
            resolvePromise(`https://${host}/${s3Key}`);
          } else {
            reject(new Error(`上传失败 HTTP ${res.statusCode}: ${body}`));
          }
        });
      }
    );
    req.on('error', reject);
    req.write(fileContent);
    req.end();
  });
}

// ========== 主函数 ==========
(async () => {
  const args = process.argv.slice(2);
  if (args.length < 1) {
    console.error('用法: node upload-s3.mjs <文件路径> [S3目录前缀]');
    process.exit(1);
  }

  const filePath = resolve(args[0]);
  if (!existsSync(filePath)) {
    console.error(`文件不存在: ${filePath}`);
    process.exit(1);
  }

  const prefix = args[1] ? args[1].replace(/\/$/g, '') + '/' : '';
  const s3Key = prefix + basename(filePath);

  console.log(`上传: ${filePath} → s3://${CONFIG.bucket}/${s3Key}`);
  console.log(`大小: ${(readFileSync(filePath).length / 1024).toFixed(1)} KB`);

  try {
    const url = await uploadFile(filePath, s3Key);
    console.log(`\n✓ 上传成功`);
    console.log(`URL: ${url}`);
  } catch (e) {
    console.error(`\n✗ ${e.message}`);
    process.exit(1);
  }
})();
