import crypto from 'crypto-js';
import {
  SHOPEE_VIDEO_VOD_KEY_PREFIX,
  SHOPEE_VIDEO_VOD_KEY_IV,
  SHOPEE_VIDEO_S3_REGION,
  SHOPEE_VIDEO_S3_SERVICE,
} from './video.constant';

/**
 * @param {ArrayBuffer} arrayBuffer
 * @returns {object}
 */
export const bufferToWordArray = (arrayBuffer) => {
  return crypto.lib.WordArray.create(arrayBuffer);
};

/**
 * @param {object|string} data
 * @returns {string}
 */
export const sha256Hex = (data) => {
  return crypto.SHA256(data).toString(crypto.enc.Hex);
};

/**
 * @param {object|string} data
 * @returns {string}
 */
export const md5Hex = (data) => {
  return crypto.MD5(data).toString(crypto.enc.Hex);
};

/**
 * @param {string} cipherB64
 * @param {number} [biz]
 * @returns {string}
 */
export const decryptVodAccessKey = (cipherB64, biz = 201) => {
  const key = crypto.enc.Utf8.parse(`${SHOPEE_VIDEO_VOD_KEY_PREFIX}${String(biz).padStart(5, '0')}`);
  const iv = crypto.enc.Utf8.parse(SHOPEE_VIDEO_VOD_KEY_IV);

  const cipherParams = crypto.lib.CipherParams.create({
    ciphertext: crypto.enc.Base64.parse(cipherB64),
  });

  const decrypted = crypto.AES.decrypt(cipherParams, key, {
    iv,
    mode: crypto.mode.CBC,
    padding: crypto.pad.Pkcs7,
  });

  return decrypted.toString(crypto.enc.Utf8);
};

/**
 * @param {string|object} data
 * @param {string|object} key
 * @returns {object}
 */
const hmac = (data, key) => {
  return crypto.HmacSHA256(data, key);
};

/**
 * @returns {string}
 */
export const amzDateNow = () => {
  const d = new Date();

  const pad = (n) => {
    return String(n).padStart(2, '0');
  };

  return (
    `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}`
    + `T${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}Z`
  );
};

/**
 * @returns {string}
 */
const randomUuid = () => {
  if (globalThis.crypto && typeof globalThis.crypto.randomUUID === 'function') {
    return globalThis.crypto.randomUUID();
  }

  return crypto.lib.WordArray.random(16).toString(crypto.enc.Hex);
};

/**
 * @param {object} payload
 * @param {string} payload.accessKey
 * @param {string} payload.secretKey
 * @param {string} payload.host
 * @param {string} payload.canonicalUri
 * @param {object} payload.bodyWordArray
 * @param {number} payload.bodyLength
 * @param {string} payload.bodyHash
 * @param {string} payload.mediaId
 * @param {string} payload.amzDate
 * @param {string} [payload.region]
 * @param {string} [payload.service]
 * @param {string} [payload.contentType]
 * @returns {object}
 */
export const buildS3PutHeaders = (payload) => {
  const {
    accessKey,
    secretKey,
    host,
    canonicalUri,
    bodyLength,
    bodyHash,
    mediaId,
    amzDate,
    region = SHOPEE_VIDEO_S3_REGION,
    service = SHOPEE_VIDEO_S3_SERVICE,
    contentType = 'video/mp4',
  } = payload;

  const datestamp = amzDate.slice(0, 8);

  const headers = {
    'amz-sdk-invocation-id': randomUuid(),
    'amz-sdk-request': 'attempt=1; max=3',
    'content-length': String(bodyLength),
    'content-type': contentType,
    host,
    'x-amz-content-sha256': bodyHash,
    'x-amz-date': amzDate,
    'x-amz-meta-mms-hasattach': 'false',
    'x-amz-meta-mms-mediaid': mediaId,
    'x-amz-user-agent': 'aws-sdk-js/3.685.0 ua/2.1 os/Windows#NT-10.0 lang/js md/browser#Electron_18.3.6 api/s3#3.685.0',
  };

  const sortedKeys = Object.keys(headers).sort();
  const signedHeaders = sortedKeys.join(';');
  const canonicalHeaders = sortedKeys.map((k) => `${k}:${headers[k]}\n`).join('');

  const canonicalRequest = ['PUT', canonicalUri, 'x-id=PutObject', canonicalHeaders, signedHeaders, bodyHash].join('\n');

  const scope = `${datestamp}/${region}/${service}/aws4_request`;
  const stringToSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonicalRequest)].join('\n');

  const kDate = hmac(datestamp, `AWS4${secretKey}`);
  const kRegion = hmac(region, kDate);
  const kService = hmac(service, kRegion);
  const kSigning = hmac('aws4_request', kService);
  const signature = crypto.HmacSHA256(stringToSign, kSigning).toString(crypto.enc.Hex);

  headers.authorization = `AWS4-HMAC-SHA256 Credential=${accessKey}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;

  return headers;
};
