const ONES = [
  '',
  'one',
  'two',
  'three',
  'four',
  'five',
  'six',
  'seven',
  'eight',
  'nine',
  'ten',
  'eleven',
  'twelve',
  'thirteen',
  'fourteen',
  'fifteen',
  'sixteen',
  'seventeen',
  'eighteen',
  'nineteen',
]

const TENS = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']

const SCALES = ['', 'thousand', 'million', 'billion', 'trillion']

// Persian number arrays
const PERSIAN_ONES = [
  '',
  'یک',
  'دو',
  'سه',
  'چهار',
  'پنج',
  'شش',
  'هفت',
  'هشت',
  'نه',
  'ده',
  'یازده',
  'دوازده',
  'سیزده',
  'چهارده',
  'پانزده',
  'شانزده',
  'هفده',
  'هجده',
  'نوزده',
]

const PERSIAN_TENS = ['', '', 'بیست', 'سی', 'چهل', 'پنجاه', 'شصت', 'هفتاد', 'هشتاد', 'نود']

const PERSIAN_HUNDREDS = [
  '',
  'صد',
  'دویست',
  'سیصد',
  'چهارصد',
  'پانصد',
  'ششصد',
  'هفتصد',
  'هشتصد',
  'نهصد',
]

const PERSIAN_SCALES = ['', 'هزار', 'میلیون', 'میلیارد', 'تریلیون']

// Persian numerals mapping
const PERSIAN_NUMERALS: { [key: string]: string } = {
  '۰': '0',
  '۱': '1',
  '۲': '2',
  '۳': '3',
  '۴': '4',
  '۵': '5',
  '۶': '6',
  '۷': '7',
  '۸': '8',
  '۹': '9',
}

// Persian numerals mapping (reverse)
const ENGLISH_TO_PERSIAN_NUMERALS: { [key: string]: string } = {
  '0': '۰',
  '1': '۱',
  '2': '۲',
  '3': '۳',
  '4': '۴',
  '5': '۵',
  '6': '۶',
  '7': '۷',
  '8': '۸',
  '9': '۹',
}

// English number words to numeric mapping
const ENGLISH_WORD_TO_NUMBER: { [key: string]: number } = {
  zero: 0,
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7,
  eight: 8,
  nine: 9,
  ten: 10,
  eleven: 11,
  twelve: 12,
  thirteen: 13,
  fourteen: 14,
  fifteen: 15,
  sixteen: 16,
  seventeen: 17,
  eighteen: 18,
  nineteen: 19,
  twenty: 20,
  thirty: 30,
  forty: 40,
  fifty: 50,
  sixty: 60,
  seventy: 70,
  eighty: 80,
  ninety: 90,
  hundred: 100,
  thousand: 1000,
  million: 1000000,
  billion: 1000000000,
  trillion: 1000000000000,
}

// Persian number words to numeric mapping
const PERSIAN_WORD_TO_NUMBER: { [key: string]: number } = {
  صفر: 0,
  یک: 1,
  دو: 2,
  سه: 3,
  چهار: 4,
  پنج: 5,
  شش: 6,
  هفت: 7,
  هشت: 8,
  نه: 9,
  ده: 10,
  یازده: 11,
  دوازده: 12,
  سیزده: 13,
  چهارده: 14,
  پانزده: 15,
  شانزده: 16,
  هفده: 17,
  هجده: 18,
  نوزده: 19,
  بیست: 20,
  سی: 30,
  چهل: 40,
  پنجاه: 50,
  شصت: 60,
  هفتاد: 70,
  هشتاد: 80,
  نود: 90,
  صد: 100,
  دویست: 200,
  سیصد: 300,
  چهارصد: 400,
  پانصد: 500,
  ششصد: 600,
  هفتصد: 700,
  هشتصد: 800,
  نهصد: 900,
  هزار: 1000,
  میلیون: 1000000,
  میلیارد: 1000000000,
  تریلیون: 1000000000000,
}

// English numerals to string mapping
const ENGLISH_NUMERALS: { [key: string]: string } = {
  '0': 'zero',
  '1': 'one',
  '2': 'two',
  '3': 'three',
  '4': 'four',
  '5': 'five',
  '6': 'six',
  '7': 'seven',
  '8': 'eight',
  '9': 'nine',
}

function convertChunk(num: number): string {
  if (num === 0) return ''

  let words = ''

  // Handle hundreds
  if (num >= 100) {
    words += ONES[Math.floor(num / 100)] + ' hundred '
    num %= 100
  }

  // Handle tens and ones
  if (num > 0) {
    if (words !== '') {
      words += 'and '
    }

    if (num < 20) {
      words += ONES[num]
    } else {
      words += TENS[Math.floor(num / 10)]
      if (num % 10 > 0) {
        words += '-' + ONES[num % 10]
      }
    }
  }

  return words.trim()
}

function convertPersianChunk(num: number): string {
  if (num === 0) return ''

  let words = ''

  // Handle hundreds
  if (num >= 100) {
    words += PERSIAN_HUNDREDS[Math.floor(num / 100)] + ' '
    num %= 100
  }

  // Handle tens and ones
  if (num > 0) {
    if (words !== '') {
      words += 'و '
    }

    if (num < 20) {
      words += PERSIAN_ONES[num]
    } else {
      words += PERSIAN_TENS[Math.floor(num / 10)]
      if (num % 10 > 0) {
        words += ' و ' + PERSIAN_ONES[num % 10]
      }
    }
  }

  return words.trim()
}

export function numberToWords(num: number): string {
  if (num === 0) return 'zero'
  if (num < 0) return 'negative ' + numberToWords(Math.abs(num))

  let words = ''
  let scaleIndex = 0

  while (num > 0) {
    const chunk = num % 1000
    if (chunk > 0) {
      const chunkWords = convertChunk(chunk)
      words = chunkWords + (SCALES[scaleIndex] ? ' ' + SCALES[scaleIndex] + ' ' : '') + words
    }
    num = Math.floor(num / 1000)
    scaleIndex++
  }

  return words.trim().toLowerCase()
}

export function numberToPersianWords(input: string | number): string {
  // Convert Persian numerals to English numerals if string input
  let num: number
  if (typeof input === 'string') {
    num = parseInt(
      input
        .split('')
        .map((char) => PERSIAN_NUMERALS[char] || char)
        .join('')
    )
  } else {
    num = input
  }

  if (isNaN(num)) {
    throw new Error('Invalid number input')
  }

  if (num === 0) return 'صفر'
  if (num < 0) return 'منفی ' + numberToPersianWords(Math.abs(num))

  let words = ''
  let scaleIndex = 0
  let hasValue = false

  while (num > 0) {
    const chunk = num % 1000
    if (chunk > 0) {
      const chunkWords = convertPersianChunk(chunk)
      if (hasValue) {
        words = chunkWords + ' ' + PERSIAN_SCALES[scaleIndex] + ' و ' + words
      } else {
        words =
          chunkWords + (PERSIAN_SCALES[scaleIndex] ? ' ' + PERSIAN_SCALES[scaleIndex] : '') + words
        hasValue = true
      }
    }
    num = Math.floor(num / 1000)
    scaleIndex++
  }

  return words.trim()
}

export function wordsToNumber(words: string): number {
  // Remove extra spaces and convert to lowercase
  words = words.toLowerCase().trim().replace(/\s+/g, ' ')

  // Handle negative numbers
  if (words.startsWith('negative ')) {
    return -wordsToNumber(words.slice(9))
  }

  let total = 0
  let currentNumber = 0

  // Split into words and process each
  const wordArray = words.split(/[\s-]+/)

  for (let i = 0; i < wordArray.length; i++) {
    const word = wordArray[i]
    if (word === 'and') continue

    const value = ENGLISH_WORD_TO_NUMBER[word]
    if (value === undefined) {
      throw new Error(`Unknown word: ${word}`)
    }

    if (value === 100) {
      currentNumber = (currentNumber || 1) * value
    } else if (value >= 1000) {
      currentNumber = (currentNumber || 1) * value
      total += currentNumber
      currentNumber = 0
    } else {
      currentNumber += value
    }
  }

  return total + currentNumber
}

export function persianWordsToNumber(words: string): number {
  // Handle negative numbers
  if (words.startsWith('منفی ')) {
    return -persianWordsToNumber(words.slice(5))
  }

  let total = 0
  let currentNumber = 0

  // Split into words and process each
  const wordArray = words.split(/\s+و\s+|\s+/)

  for (let i = 0; i < wordArray.length; i++) {
    const word = wordArray[i].trim()
    const value = PERSIAN_WORD_TO_NUMBER[word]

    if (value === undefined) {
      throw new Error(`Unknown word: ${word}`)
    }

    if (value >= 1000) {
      currentNumber = (currentNumber || 1) * value
      total += currentNumber
      currentNumber = 0
    } else {
      currentNumber += value
    }
  }

  return total + currentNumber
}

export function stringToNumber(input: string): number {
  // Try parsing as a regular number first
  const num = Number(input)
  if (!isNaN(num)) {
    return num
  }

  // Convert Persian numerals to English numerals
  const englishNumerals = input
    .split('')
    .map((char) => PERSIAN_NUMERALS[char] || char)
    .join('')
  const numFromPersian = Number(englishNumerals)
  if (!isNaN(numFromPersian)) {
    return numFromPersian
  }

  // Try parsing as English words
  try {
    return wordsToNumber(input)
  } catch (e) {
    // Try parsing as Persian words
    try {
      return persianWordsToNumber(input)
    } catch (e2) {
      throw new Error('Invalid number format')
    }
  }
}

export function numberToString(num: number, format: 'english' | 'persian' = 'english'): string {
  if (format === 'persian') {
    return numberToPersianWords(num)
  }
  return numberToWords(num)
}

export function numberToPersianNumerals(num: number): string {
  return num
    .toString()
    .split('')
    .map((digit) => ENGLISH_TO_PERSIAN_NUMERALS[digit] || digit)
    .join('')
}

export function persianNumeralsToNumber(persianNumerals: string): number {
  const englishNumerals = persianNumerals
    .split('')
    .map((char) => PERSIAN_NUMERALS[char] || char)
    .join('')
  return parseInt(englishNumerals)
}

export default {
  numberToWords,
  numberToPersianWords,
  wordsToNumber,
  persianWordsToNumber,
  stringToNumber,
  numberToString,
  numberToPersianNumerals,
  persianNumeralsToNumber,
}
