/* eslint-env node */
import { default as babelExtractor } from '@lingui/cli/api/extractors/babel'
import { createHash } from 'crypto'
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
import { existsSync } from 'fs'
import * as path from 'path'

/** A custom caching extractor built on top of babelExtractor. */
const cachingExtractor: typeof babelExtractor = {
  /** Delegates to babelExtractor.match. */
  match(filename: string) {
    return babelExtractor.match(filename)
  },
  /**
   * Checks a cache before extraction, only delegating to babelExtractor.extract if the file has changed.
   *
   * The lingui extractor works by extracting JSON (the catalog) from `filename` to `buildDir/filename.json`.
   * Caching works by man-in-the-middling this:
   * - File freshness is computed as a hash of `filename` contents.
   * - Before extracting, we check the cache to see if we already have a fresh catalog for the file.
   *   If we do, we copy it to `localeDir/filename.json`. Copying is significantly faster than extracting.
   * - After extracting, we copy the catalog to the cache.
   */
  extract(filename: string, localeDir: string, ...options: unknown[]) {
    // This runs from node_modules/@lingui/conf, so we need to back out to the root.
    const root = __dirname.split('/node_modules')[0]

    // This logic mimics catalogFilename in @lingui/babel-plugin-extract-messages.
    const buildDir = path.join(localeDir, '_build')
    const localePath = path.join(buildDir, filename + '.json')

    const filePath = path.join(root, filename)
    const fileHash = createHash('sha256').update(readFileSync(filePath)).digest('hex')

    const cacheRoot = path.join(root, 'node_modules/.cache/lingui')
    const cachePath = path.join(cacheRoot, filename + '.json')

    // If we have a matching cached copy of the catalog, we can copy it to localePath and return early.
    if (existsSync(cachePath)) {
      const { hash, catalog } = JSON.parse(readFileSync(cachePath, 'utf8'))
      if (hash === fileHash) {
        if (catalog) {
          mkdirSync(path.dirname(localePath), { recursive: true })
          writeFileSync(localePath, JSON.stringify(catalog, null, 2))
        }
        return
      }
    }

    babelExtractor.extract(filename, localeDir, ...options)

    // Cache the extracted catalog.
    mkdirSync(path.dirname(cachePath), { recursive: true })
    if (existsSync(localePath)) {
      const catalog = JSON.parse(readFileSync(localePath, 'utf8'))
      writeFileSync(cachePath, JSON.stringify({ hash: fileHash, catalog }))
    } else {
      writeFileSync(cachePath, JSON.stringify({ hash: fileHash }))
    }
  },
}

const linguiConfig = {
  catalogs: [
    {
      path: '<rootDir>/src/locales/{locale}',
      include: ['<rootDir>/src/**/*.ts', '<rootDir>/src/**/*.tsx'],
      exclude: [
        '<rootDir>/src/**/*.d.ts',
        '<rootDir>/src/**/*.test.*',
        '<rootDir>/src/types/v3/**',
        '<rootDir>/src/abis/types/**',
        '<rootDir>/src/graphql/**/__generated__/**',
      ],
    },
  ],
  compileNamespace: 'cjs',
  fallbackLocales: {
    default: 'en-US',
  },
  format: 'po',
  formatOptions: {
    lineNumbers: false,
  },
  locales: [
    'af-ZA',
    'ar-SA',
    'ca-ES',
    'cs-CZ',
    'da-DK',
    'de-DE',
    'el-GR',
    'en-US',
    'es-ES',
    'fi-FI',
    'fr-FR',
    'he-IL',
    'hu-HU',
    'id-ID',
    'it-IT',
    'ja-JP',
    'ko-KR',
    'nl-NL',
    'no-NO',
    'pl-PL',
    'pt-BR',
    'pt-PT',
    'ro-RO',
    'ru-RU',
    'sr-SP',
    'sv-SE',
    'sw-TZ',
    'tr-TR',
    'uk-UA',
    'vi-VN',
    'zh-CN',
    'zh-TW',
  ],
  orderBy: 'messageId',
  rootDir: '.',
  runtimeConfigModule: ['@lingui/core', 'i18n'],
  sourceLocale: 'en-US',
  extractors: [cachingExtractor],
}

export default linguiConfig
