const path = require('path')
const { hasBilditTemplateVars } = require('./preprocessBilditTemplate')

function escapeRegex(s) {
  return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

/** Same as library add: basename without extension, strip trailing `.template`. */
function extractTemplateIdFromPath(filePath) {
  const basename = path.basename(filePath, path.extname(filePath))
  return basename.replace(/\.template$/, '')
}

function filenameHasDotTemplateSegment(filePath) {
  return path.basename(filePath).includes('.template.')
}

/**
 * @param {string} code
 * @param {string} templateId
 * @returns {string|null} version token after `v`, e.g. `1.0` or `2`
 */
function parseTopTemplateVersionComment(code, templateId) {
  if (!code || !templateId) return null
  const lines = code.split(/\r?\n/).slice(0, 25)
  const re = new RegExp(`^//\\s*${escapeRegex(templateId)}\\s*:\\s*v([\\d.]+)\\b`, 'i')
  for (const line of lines) {
    const m = line.trim().match(re)
    if (m) return m[1]
  }
  return null
}

/**
 * @param {string|null} previousVersion
 * @returns {string} next display: first `1.0`, then `2`, `3`, …
 */
function nextTemplateVersionDisplay(previousVersion) {
  if (previousVersion == null || previousVersion === '') return '1.0'
  const major = parseInt(String(previousVersion).split('.')[0], 10)
  const n = Number.isFinite(major) && major > 0 ? major : 1
  return String(n + 1)
}

/**
 * @param {string} templateId
 * @param {string} versionDisplay
 * @param {boolean} legacy - true for `template legacy from-jsx|from-tsx`
 */
function formatTemplateVersionBannerLine(templateId, versionDisplay, legacy) {
  const leg = legacy ? 'legacy=true' : 'legacy=false'
  return `// ${templateId}:v${versionDisplay} ${leg}`
}

/**
 * Insert `// @template v2` banner immediately after our version line when present.
 * @param {string} code
 * @param {string} bannerLine
 */
function prependTemplateVersionBanner(code, bannerLine) {
  const v2Banner = /^(\uFEFF?)(\s*\/\/\s*@template\s+v2\b[^\n]*\n+)/
  const m = code.match(v2Banner)
  if (m) {
    return m[1] + bannerLine + '\n' + m[2] + code.slice(m[0].length)
  }
  return `${bannerLine}\n${code}`
}

/**
 * v1 `$()` fields or v2 JSDoc `@type` template fields (codemod / hand-written).
 * @param {string} code
 */
const V2_JSDOC_TYPES = 'RichText|Color|String|Boolean|Number|Image|Link|BannerId|Component'

function codeAppearsToHaveEditableTemplateFields(code) {
  if (!code || typeof code !== 'string') return false
  if (/\$\([^)]+\)/.test(code)) return true
  // v2 codemod: `* @type RichText`; some hand-written JSDoc uses `@type {String}`
  if (new RegExp(`@type\\s+(?:${V2_JSDOC_TYPES})\\b`).test(code)) return true
  if (new RegExp(`@type\\s*\\{\\s*(?:${V2_JSDOC_TYPES})\\s*\\}`).test(code)) return true
  return false
}

const NOT_A_BILDIT_TEMPLATE_MESSAGE =
  'This file does not look like a BILDIT web template (no v1 `$(…)` fields and no v2 JSDoc `@type` fields).\n\n' +
  'Scaffold one first, for example:\n' +
  '  bild template from-jsx <Component.jsx> …\n' +
  '  bild template from-tsx <Component.tsx> …\n' +
  'For legacy `$(…)` syntax with // group / // endgroup:\n' +
  '  bild template legacy from-jsx <Component.jsx> …\n' +
  '  bild template legacy from-tsx <Component.tsx> …'

/**
 * @param {string} code
 * @param {string} codeType - js|jsx|ts|tsx|html
 * @throws {Error} when web code has no recognizable CMS-editable fields
 */
function assertFileIsBilditWebTemplate(code, codeType) {
  if (codeType === 'html') return
  if (!codeAppearsToHaveEditableTemplateFields(code)) {
    const err = new Error(NOT_A_BILDIT_TEMPLATE_MESSAGE)
    err.code = 'BILD_NOT_A_TEMPLATE'
    throw err
  }
}

/**
 * Short line for CLI output after assertFileIsBilditWebTemplate.
 * @param {string} code
 * @param {string} codeType
 * @returns {string}
 */
function describeBilditTemplateKindForLog(code, codeType) {
  if (codeType === 'html') {
    return 'Template kind: HTML (skipping v1/v2 field detection).'
  }
  if (hasBilditTemplateVars(code)) {
    return 'Template kind: legacy v1 (`$(…)` CMS fields).'
  }
  return 'Template kind: v2 (JSDoc `@type` fields / `// @template v2`).'
}

/**
 * `.template.` in filename and a matching `// {templateId}:v…` comment near the top.
 * Used to default library add to skip ESLint (transpile still runs).
 */
function shouldDefaultNoLintLibraryAdd(filePath, code) {
  if (!filenameHasDotTemplateSegment(filePath)) return false
  const id = extractTemplateIdFromPath(filePath)
  return parseTopTemplateVersionComment(code, id) != null
}

function totalTaggedFieldCount(fieldReport) {
  if (!fieldReport) return 0
  return (
    (fieldReport.richTextCount || 0) +
    (fieldReport.colorCount || 0) +
    (fieldReport.stringCount || 0) +
    (fieldReport.booleanCount || 0) +
    (fieldReport.numberCount || 0) +
    (fieldReport.moduleArrayFieldCount || 0)
  )
}

module.exports = {
  extractTemplateIdFromPath,
  filenameHasDotTemplateSegment,
  parseTopTemplateVersionComment,
  nextTemplateVersionDisplay,
  formatTemplateVersionBannerLine,
  prependTemplateVersionBanner,
  codeAppearsToHaveEditableTemplateFields,
  assertFileIsBilditWebTemplate,
  describeBilditTemplateKindForLog,
  NOT_A_BILDIT_TEMPLATE_MESSAGE,
  shouldDefaultNoLintLibraryAdd,
  totalTaggedFieldCount,
}
