import * as plugins from './plugins.js';
import { sha256Fallback } from './sha256.fallback.js';

/**
 * Convert ArrayBuffer to hex string
 */
const hex = (buffer: ArrayBuffer): string => {
  const hexCodes: string[] = [];
  const view = new DataView(buffer);
  for (let i = 0; i < view.byteLength; i += 4) {
    // Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
    const value = view.getUint32(i);
    // toString(16) will give the hex representation of the number without padding
    const stringValue = value.toString(16);
    // We use concatenation and slice for padding
    const padding = '00000000';
    const paddedValue = (padding + stringValue).slice(-padding.length);
    hexCodes.push(paddedValue);
  }

  // Join all the hex strings into one
  return hexCodes.join("");
};

/**
 * Check if crypto.subtle is available
 */
const isCryptoSubtleAvailable = (): boolean => {
  return typeof crypto !== 'undefined' && crypto.subtle !== undefined;
};

/**
 * Computes sha256 Hash from String
 */
export const sha256FromString = async (stringArg: string): Promise<string> => {
  // Get the string as arraybuffer.
  const buffer = (new TextEncoder()).encode(stringArg);
  
  if (isCryptoSubtleAvailable()) {
    const hash = await crypto.subtle.digest("SHA-256", buffer);
    const result = hex(hash);
    return result;
  } else {
    // Use fallback for non-HTTPS environments
    return sha256Fallback(buffer);
  }
};

/**
 * Computes sha256 Hash from String synchronously
 * Note: In browser environment, this is still async internally but we maintain the API
 */
export const sha256FromStringSync = (stringArg: string): string => {
  console.warn('sha256FromStringSync is not truly synchronous in browser environment');
  throw new Error('sha256FromStringSync is not supported in browser environment. Use sha256FromString instead.');
};

/**
 * Computes sha256 Hash from ArrayBuffer
 */
export const sha256FromBuffer = async (bufferArg: ArrayBuffer | Uint8Array): Promise<string> => {
  if (isCryptoSubtleAvailable()) {
    // Ensure we pass an ArrayBuffer to satisfy BufferSource typing
    let inputBuffer: ArrayBuffer;
    if (bufferArg instanceof Uint8Array) {
      const view = bufferArg;
      inputBuffer = view.buffer.slice(
        view.byteOffset,
        view.byteOffset + view.byteLength
      ) as ArrayBuffer;
    } else {
      inputBuffer = bufferArg;
    }

    const hash = await crypto.subtle.digest("SHA-256", inputBuffer);
    const result = hex(hash);
    return result;
  } else {
    // Use fallback for non-HTTPS environments
    const uint8Array = bufferArg instanceof Uint8Array ? bufferArg : new Uint8Array(bufferArg);
    return sha256Fallback(uint8Array);
  }
};

/**
 * computes sha265 Hash from Object
 */
export const sha265FromObject = async (objectArg: any): Promise<string> => {
  const stringifiedObject = plugins.smartjson.stringify(objectArg);
  const hashResult = await sha256FromString(stringifiedObject);
  return hashResult;
};

/**
 * creates sha256 Hash from Stream
 * Note: Not supported in browser environment
 */
export const sha256FromStream = (input: any): Promise<string> => {
  throw new Error('sha256FromStream is not supported in browser environment');
};

/**
 * creates sha256 Hash from File
 * Note: Not supported in browser environment
 */
export const sha256FromFile = async (filePath: string): Promise<string> => {
  throw new Error('sha256FromFile is not supported in browser environment');
};

/**
 * Computes MD5 Hash from String
 * Note: MD5 is not natively supported by Web Crypto API
 */
export const md5FromString = async (stringToHash: string): Promise<string> => {
  throw new Error('md5FromString is not supported in browser environment. Web Crypto API does not support MD5.');
};
