const parser = require('@babel/parser')
const traverseMod = require('@babel/traverse')
const generateMod = require('@babel/generator')
const { ensureDefaultExport, programHasDefaultExport } = require('./codemod/field-hoist')
const { parserPluginsFor, normalizeTSTypeCastExpressionsForGenerator } = require('./codemod/utils')
const {
  preprocessBilditTemplate,
  canonicalizeDoubleParenAsCastTemplates,
} = require('./preprocessBilditTemplate')

const traverse = traverseMod.default || traverseMod
const generate = generateMod.default || generateMod

const NO_DEFAULT_EXPORT =
  'No default export found. Make sure your component uses "export default".'

/**
 * Babel `parserPluginsFor` / codemod use "tsx" for any TypeScript file (`.ts` and `.tsx`).
 * @param {string} codeType
 * @returns {string|undefined}
 */
function parseKindFromWebCodeType(codeType) {
  if (codeType === 'html') return null
  if (codeType === 'ts' || codeType === 'tsx') return 'tsx'
  return 'jsx'
}

/**
 * Parse → ensure a `export default` the CMS can load (banners, templates) → regenerate.
 * When the only export is a PascalCase named component, appends `export default Name`
 * (same as template codemod). HTML is unchanged.
 *
 * @param {string} source
 * @param {string} codeType
 * @returns {{ code: string, didAdd: boolean }}
 */
function parseForCodeLibraryExportCheck(source, codeType) {
  const parseKind = parseKindFromWebCodeType(codeType)
  const sourceForParse = preprocessBilditTemplate(
    canonicalizeDoubleParenAsCastTemplates(source, parseKind),
    parseKind,
  )
  return parser.parse(sourceForParse, {
    sourceType: 'module',
    plugins: parserPluginsFor(parseKind),
    errorRecovery: true,
  })
}

/**
 * Whether the module has a default export, after the same preprocessing used for lint/transpile.
 * Does not mutate or re-print source (library add/update must upload the original template text).
 */
function codeLibraryHasDefaultExport(source, codeType) {
  if (codeType === 'html') {
    return true
  }
  const ast = parseForCodeLibraryExportCheck(source, codeType)
  return programHasDefaultExport(ast, traverse)
}

function ensureCodeLibraryDefaultExport(source, codeType) {
  if (codeType === 'html') {
    return { code: source, didAdd: false }
  }
  const ast = parseForCodeLibraryExportCheck(source, codeType)
  const didAdd = ensureDefaultExport(ast, traverse)
  if (!programHasDefaultExport(ast, traverse)) {
    const err = new Error(NO_DEFAULT_EXPORT)
    err.code = 'BILD_NO_DEFAULT_EXPORT'
    throw err
  }
  if (codeType === 'ts' || codeType === 'tsx') {
    normalizeTSTypeCastExpressionsForGenerator(ast, traverse)
  }
  const code = generate(ast, {
    retainLines: false,
    comments: true,
    compact: false,
  }).code
  return { code, didAdd }
}

module.exports = {
  ensureCodeLibraryDefaultExport,
  codeLibraryHasDefaultExport,
  NO_DEFAULT_EXPORT,
}
