
import { logger } from './logger';

/**
 *
 * @param {string|number} timestamp 
 * @param {boolean} debug 
 * @returns {number} 
 */
export function standardizeTimestamp(timestamp: string | number, debug = false): number {
  if (timestamp === undefined || timestamp === null) {
    if (debug) logger.debug('Timestamp undefined or null, using current time');
    return Date.now();
  }

  const timestampNum = typeof timestamp === 'string' ? parseInt(timestamp, 10) : timestamp;
  

  if (debug) logger.debug(`Standardizing timestamp: original=${timestamp}, parsed=${timestampNum}`);
  
  if (timestampNum < 10000000000) {
    const converted = timestampNum * 1000;
    if (debug) logger.debug(`Converting seconds to milliseconds: ${timestampNum} → ${converted} (${new Date(converted).toISOString()})`);
    return converted;
  }
  
  if (debug) logger.debug(`Timestamp already in milliseconds: ${timestampNum} (${new Date(timestampNum).toISOString()})`);
  return timestampNum;
}

/**
 * 
 * @param {string|number|Date} timestamp 
 * @returns {Date} 
 * @throws {Error} 
 */
export function parseBlockchainTimestamp(timestamp: string | number | Date): Date {
  if (!timestamp) {
    throw new Error('Invalid timestamp: empty value');
  }

  let date: Date;
  
  if (timestamp instanceof Date) {
    date = timestamp;
  } else if (typeof timestamp === 'string') {
    if (timestamp.startsWith('0x')) {
      const seconds = parseInt(timestamp, 16);
      date = new Date(seconds * 1000);
    } else if (/^\d+$/.test(timestamp)) {
      const num = parseInt(timestamp, 10);
      if (num < 10000000000) {
        date = new Date(num * 1000);
      } else {
        date = new Date(num);
      }
    } else if (timestamp.includes('T') && timestamp.includes('-')) {
      date = new Date(timestamp);
    } else if (timestamp.includes('/') || timestamp.includes('-')) {
      date = new Date(timestamp);
    } else {
      date = new Date(timestamp);
    }
  } else {

    if (timestamp < 10000000000) {
      date = new Date(timestamp * 1000);
    } else {
      date = new Date(timestamp);
    }
  }
  
  if (isNaN(date.getTime())) {
    throw new Error(`Invalid timestamp: could not parse date from ${JSON.stringify(timestamp)}`);
  }
  
  return date;
}

/**

 * 
 * @param {string|number|Date} timestamp 
 * @param {boolean} fallbackToNow 
 * @returns {number}
 */
export function parseTimestampToMillis(timestamp: string | number | Date, fallbackToNow = true): number {
  try {
    const date = parseBlockchainTimestamp(timestamp);
    return date.getTime();
  } catch (error) {
    logger.error('Error parsing timestamp:', error, timestamp);
    return fallbackToNow ? Date.now() : 0;
  }
}

/**

 * 
 * @param {string|number|Date} timestamp 
 * @returns {number} 
 */
export function calculateAgeInMillis(timestamp: string | number | Date): number {
  const timestampMs = parseTimestampToMillis(timestamp);
  return Date.now() - timestampMs;
}

/**
 * 
 * @param {string|number|Date} timestamp 
 * @returns {string} 
 */
export function formatRelativeTime(timestamp: string | number | Date): string {
  if (!timestamp) {
    return 'Unknown time';
  }

  let date: Date;
  
  try {
    date = parseBlockchainTimestamp(timestamp);
  } catch (error) {
    logger.error('Error parsing timestamp:', error, timestamp);
    return 'Invalid date';
  }

  const now = new Date();
  const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);

  if (isNaN(diffInSeconds)) {
    return 'Invalid date';
  }

  let relativeTime = '';
  if (diffInSeconds < 5) {
    relativeTime = 'Just now';
  } else if (diffInSeconds < 60) {
    relativeTime = `${diffInSeconds} ${diffInSeconds === 1 ? 'sec' : 'secs'} ago`;
  } else {
    const diffInMinutes = Math.floor(diffInSeconds / 60);
    if (diffInMinutes < 60) {
      relativeTime = `${diffInMinutes} ${diffInMinutes === 1 ? 'min' : 'mins'} ago`;
    } else {
      const diffInHours = Math.floor(diffInMinutes / 60);
      if (diffInHours < 24) {
        relativeTime = `${diffInHours} ${diffInHours === 1 ? 'hour' : 'hours'} ago`;
      } else {
        const diffInDays = Math.floor(diffInHours / 24);
        if (diffInDays < 30) {
          relativeTime = `${diffInDays} ${diffInDays === 1 ? 'day' : 'days'} ago`;
        } else {
          const diffInMonths = Math.floor(diffInDays / 30);
          if (diffInMonths < 12) {
            relativeTime = `${diffInMonths} ${diffInMonths === 1 ? 'month' : 'months'} ago`;
          } else {
            const diffInYears = Math.floor(diffInMonths / 12);
            relativeTime = `${diffInYears} ${diffInYears === 1 ? 'year' : 'years'} ago`;
          }
        }
      }
    }
  }

  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const year = date.getUTCFullYear();
  const month = months[date.getUTCMonth()];
  const day = date.getUTCDate().toString().padStart(2, '0');
  
  let hours = date.getUTCHours();
  const ampm = hours >= 12 ? 'PM' : 'AM';
  hours = hours % 12;
  hours = hours ? hours : 12;
  
  const minutes = date.getUTCMinutes().toString().padStart(2, '0');
  const seconds = date.getUTCSeconds().toString().padStart(2, '0');
  
  const utcTime = `${month}-${day}-${year} ${hours.toString().padStart(2, '0')}:${minutes}:${seconds} ${ampm} +UTC`;
  
  return `${relativeTime} (${utcTime})`;
}

/**

 * @param {string|number|Date} timestamp 
 * @returns {string} 
 */
export function formatTableTime(timestamp: string | number | Date): string {
  if (!timestamp) {
    return 'Unknown time';
  }

  try {
    const date = parseBlockchainTimestamp(timestamp);
    
    const now = new Date();
    const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);

    if (isNaN(diffInSeconds)) {
      return 'Invalid date';
    }

    if (diffInSeconds < 5) {
      return 'Just now';
    }
    
    if (diffInSeconds < 60) {
      return `${diffInSeconds} ${diffInSeconds === 1 ? 'sec' : 'secs'} ago`;
    }

    const diffInMinutes = Math.floor(diffInSeconds / 60);
    if (diffInMinutes < 60) {
      return `${diffInMinutes} ${diffInMinutes === 1 ? 'min' : 'mins'} ago`;
    }

    const diffInHours = Math.floor(diffInMinutes / 60);
    if (diffInHours < 24) {
      return `${diffInHours} ${diffInHours === 1 ? 'hour' : 'hours'} ago`;
    }

    const diffInDays = Math.floor(diffInHours / 24);
    if (diffInDays < 30) {
      return `${diffInDays} ${diffInDays === 1 ? 'day' : 'days'} ago`;
    }

    const diffInMonths = Math.floor(diffInDays / 30);
    if (diffInMonths < 12) {
      return `${diffInMonths} ${diffInMonths === 1 ? 'month' : 'months'} ago`;
    }

    const diffInYears = Math.floor(diffInMonths / 12);
    return `${diffInYears} ${diffInYears === 1 ? 'year' : 'years'} ago`;
  } catch (error) {
    logger.error('Error parsing timestamp:', error, timestamp);
    return 'Invalid date';
  }
}

/**
 * 
 * @param {number} value 
 * @param {number} decimals 
 * @returns {string} 
 */
export function formatNumber(value: number, decimals = 2): string {
  if (!value && value !== 0) return 'N/A';
  
  if (value === 0) return '0';
  
  if (Math.abs(value) < 1000) {
    return value.toFixed(decimals).replace(/\.0+$/, '');
  }
  
  const suffixes = ['', 'K', 'M', 'B', 'T'];
  const suffixIndex = Math.floor(Math.log10(Math.abs(value)) / 3);
  
  if (suffixIndex >= suffixes.length) {
    return value.toExponential(decimals);
  }
  
  const scaledValue = value / Math.pow(10, suffixIndex * 3);
  return `${scaledValue.toFixed(decimals).replace(/\.0+$/, '')}${suffixes[suffixIndex]}`;
}

/**
 * 
 * @param {string|number} wei 
 * @param {number} decimals 
 * @returns {string} 
 */
export function formatWeiToEth(wei: string | number, decimals = 6): string {
  if (!wei) return '0 tEXT';
  
  try {
    const weiValue = typeof wei === 'string' && wei.startsWith('0x')
      ? parseInt(wei, 16)
      : Number(wei);
    
    const ethValue = weiValue / 1e18;
    
    if (ethValue === Math.floor(ethValue)) {
      return `${Math.floor(ethValue)} tEXT`;
    }
    
    const formatted = ethValue.toFixed(decimals);
    
    const trimmed = formatted.replace(/\.?0+$/, '');
    
    return `${trimmed} tEXT`;
  } catch (e) {
    logger.error('Error formatting wei to tEXT:', e);
    return '0 tEXT';
  }
}

/**
 * 
 * @param {string} str 
 * @param {number} frontChars 
 * @param {number} endChars 
 * @returns {string} 
 */
export function truncateString(str: string, frontChars = 6, endChars = 4): string {
  if (!str) return '';
  
  if (str.length <= frontChars + endChars) return str;
  
  return `${str.substring(0, frontChars)}...${str.substring(str.length - endChars)}`;
}

/**
 * 
 * @param {string} address 
 * @returns {string} 
 */
export function formatAddress(address: string): string {
  if (!address) return '';
  
  return truncateString(address, 6, 4);
}

/**
 * 
 * @param {string|number} rawAmount 
 * @param {number} decimals 
 * @param {string} symbol 
 * @returns {string} 
 */
export function formatTokenAmount(rawAmount: string | number, decimals: number = 18, symbol: string = ''): string {
  if (!rawAmount) return `0${symbol ? ' ' + symbol : ''}`;
  

  let numericAmount: number;
  if (typeof rawAmount === 'string') {
    if (rawAmount.startsWith('0x')) {
      numericAmount = parseInt(rawAmount, 16) / Math.pow(10, decimals);
    } else {
      numericAmount = Number(rawAmount) / Math.pow(10, decimals);
    }
  } else {
    numericAmount = rawAmount / Math.pow(10, decimals);
  }
  
  return `${numericAmount.toLocaleString(undefined, {
    maximumFractionDigits: numericAmount > 1000 ? 0 : 2,
    minimumFractionDigits: 0
  })}${symbol ? ' ' + symbol : ''}`;
}

/**
 * 
 * @param {string|number} hexValue 
 * @param {number} defaultValue 
 * @returns {number} 
 */
export function parseHexToDecimal(hexValue: string | number | undefined, defaultValue = 0): number {
  try {
    if (hexValue === undefined || hexValue === null) {
      return defaultValue;
    }
    
    if (typeof hexValue === 'number') {
      return hexValue;
    }
    
    if (typeof hexValue === 'string') {
      if (hexValue.startsWith('0x')) {
        return parseInt(hexValue, 16);
      }
      return parseInt(hexValue, 10);
    }
    
    return defaultValue;
  } catch (error) {
    logger.error('Error parsing hex value:', error, hexValue);
    return defaultValue;
  }
}

/**
 * 
 * @param {string|number} value 
 * @param {string} unit 
 * @param {number} decimals 
 * @returns {string} 
 */
export function formatGasValue(value: string | number | undefined, unit = 'gwei', decimals = 2): string {
  try {
    if (value === undefined || value === null) {
      return 'N/A';
    }
    
    const weiValue = parseHexToDecimal(value);
    
    if (weiValue === 0) {
      return `0 ${unit.toUpperCase()}`;
    }
    
    let convertedValue = weiValue;
    let unitLabel = unit.toUpperCase();
    
    if (unit.toLowerCase() === 'gwei') {
      convertedValue = weiValue / 1e9;
      unitLabel = 'Gwei';
    } else if (unit.toLowerCase() === 'text') {
      convertedValue = weiValue / 1e18;
      unitLabel = 'TEXT';
    } else {
      unitLabel = 'Wei';
    }
    
    return `${convertedValue.toFixed(decimals).replace(/\.0+$/, '')} ${unitLabel}`;
  } catch (error) {
    logger.error('Error formatting gas value:', error, value);
    return 'N/A';
  }
}

/**
 * 
 * @param {string|number} gasUsed 
 * @param {string|number} gasLimit 
 * @returns {number} 
 */
export function calculateGasPercentage(gasUsed: string | number | undefined, gasLimit: string | number | undefined): number {
  const gasUsedDecimal = parseHexToDecimal(gasUsed);
  const gasLimitDecimal = parseHexToDecimal(gasLimit);
  
  if (gasLimitDecimal === 0) {
    return 0;
  }
  
  return Math.round((gasUsedDecimal / gasLimitDecimal) * 100);
}

/**
 * 
 * @param {number} value 
 * @returns {string} 
 */
export function formatDecimalToHex(value: number): string {
  return '0x' + value.toString(16);
} 