import { exec } from 'child_process'
import * as fs from 'fs/promises'
import * as linguistLanguages from 'linguist-languages'
import * as path from 'path'
import { promisify } from 'util'

const execAsync = promisify(exec)

type LanguageData = {
  type: string
  aliases?: string[]
  extensions?: string[]
}

const LANGUAGES_FILE_PATH = path.join(__dirname, '../packages/shared/config/languages.ts')

/**
 * Extracts and filters necessary language data from the linguist-languages package.
 * @returns A record of language data.
 */
function extractAllLanguageData(): Record<string, LanguageData> {
  console.log('🔍 Extracting language data from linguist-languages...')
  const languages = Object.entries(linguistLanguages).reduce(
    (acc, [name, langData]) => {
      const { type, extensions, aliases } = langData as any

      // Only include languages with extensions or aliases
      if ((extensions && extensions.length > 0) || (aliases && aliases.length > 0)) {
        acc[name] = {
          type: type || 'programming',
          ...(extensions && { extensions }),
          ...(aliases && { aliases })
        }
      }
      return acc
    },
    {} as Record<string, LanguageData>
  )
  console.log(`✅ Extracted ${Object.keys(languages).length} languages.`)
  return languages
}

/**
 * Generates the content for the languages.ts file.
 * @param languages The language data to include in the file.
 * @returns The generated file content as a string.
 */
function generateLanguagesFileContent(languages: Record<string, LanguageData>): string {
  console.log('📝 Generating languages.ts file content...')
  const sortedLanguages = Object.fromEntries(Object.entries(languages).sort(([a], [b]) => a.localeCompare(b)))

  const languagesObjectString = JSON.stringify(sortedLanguages, null, 2)

  const content = `/**
 * Code language list.
 * Data source: linguist-languages
 *
 * ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
 *   THIS FILE IS AUTOMATICALLY GENERATED BY A SCRIPT. DO NOT EDIT IT MANUALLY!
 *   Run \`pnpm update:languages\` to update this file.
 * ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
 *
 */

type LanguageData = {
  type: string;
  aliases?: string[];
  extensions?: string[];
};

export const languages: Record<string, LanguageData> = ${languagesObjectString};
`
  console.log('✅ File content generated.')
  return content
}

/**
 * Formats a file using Biome.
 * @param filePath The path to the file to format.
 */
async function format(filePath: string): Promise<void> {
  console.log('🎨 Formatting file with Biome...')
  try {
    await execAsync(`pnpm biome format --write ${filePath}`)
    console.log('✅ Biome formatting complete.')
  } catch (e: any) {
    console.error('❌ Biome formatting failed:', e.stdout || e.stderr)
    throw new Error('Biome formatting failed.')
  }
}

/**
 * Checks a file with TypeScript compiler.
 * @param filePath The path to the file to check.
 */
async function checkTypeScript(filePath: string): Promise<void> {
  console.log('🧐 Checking file with TypeScript compiler...')
  try {
    await execAsync(`pnpm tsc --noEmit --skipLibCheck ${filePath}`)
    console.log('✅ TypeScript check passed.')
  } catch (e: any) {
    console.error('❌ TypeScript check failed:', e.stdout || e.stderr)
    throw new Error('TypeScript check failed.')
  }
}

/**
 * Main function to update the languages.ts file.
 */
async function updateLanguagesFile(): Promise<void> {
  console.log('🚀 Starting to update languages.ts...')
  try {
    const extractedLanguages = extractAllLanguageData()
    const fileContent = generateLanguagesFileContent(extractedLanguages)

    await fs.writeFile(LANGUAGES_FILE_PATH, fileContent, 'utf-8')
    console.log(`✅ Successfully wrote to ${LANGUAGES_FILE_PATH}`)

    await format(LANGUAGES_FILE_PATH)
    await checkTypeScript(LANGUAGES_FILE_PATH)

    console.log('🎉 Successfully updated languages.ts file!')
    console.log(`📊 Contains ${Object.keys(extractedLanguages).length} languages.`)
  } catch (error) {
    console.error('❌ An error occurred during the update process:', (error as Error).message)
    // No need to restore backup as we write only at the end of successful generation.
    process.exit(1)
  }
}

if (require.main === module) {
  void updateLanguagesFile()
}

export { updateLanguagesFile }
