const path = require('path')
const { ESLint } = require('eslint')

/** Directory of the published `bild` package (where CLI deps are installed). */
const BILD_CLI_ROOT = path.join(__dirname, '..', '..')

/**
 * Resolve a package entrypoint from the CLI's own node_modules so ESLint/Babel
 * never load parsers or presets from the consumer project's tree (which may not
 * have @babel/eslint-parser).
 */
function resolveFromCli(specifier) {
  return require.resolve(specifier, { paths: [BILD_CLI_ROOT] })
}

function babelPresetsForFile(filePath) {
  const presets = [resolveFromCli('@babel/preset-env')]
  if (filePath && /\.tsx?$/i.test(filePath)) {
    presets.push([
      resolveFromCli('@babel/preset-typescript'),
      { isTSX: /\.tsx$/i.test(filePath), allExtensions: true },
    ])
  }
  presets.push(resolveFromCli('@babel/preset-react'))
  return presets
}

/**
 * `no-undef` from eslint:recommended does not understand TypeScript (`as const`,
 * types, etc.) and false-positives. TS already reports undefined identifiers.
 */
function eslintRulesForFile(filePath) {
  const rules = {
    'no-unused-vars': 'warn',
    'no-console': 'off',
  }
  if (filePath && /\.tsx?$/i.test(filePath)) {
    rules['no-undef'] = 'off'
  }
  return rules
}

// Tailwind class validation regex
const TAILWIND_CLASS_REGEX = /(?:^|\s)(?:[a-z]+:)?(?:-?[a-z]+(?:-[a-z]+)*(?::[a-z]+(?:-[a-z]+)*)?)(?=\s|$)/g

// Template syntax validation
const TEMPLATE_VAR_REGEX = /\$\([^)]+\)/g

/**
 * @param {string} code
 * @param {'auto'|'browser'|'node'|'both'|undefined|null} lintEnv
 * @returns {'browser'|'node'|'both'}
 */
function resolveLintEnv(code, lintEnv) {
  const requested = lintEnv ? String(lintEnv).trim().toLowerCase() : 'auto'
  if (requested === 'browser' || requested === 'node' || requested === 'both') {
    return requested
  }
  // auto
  const text = code == null ? '' : String(code)
  // Next.js/React client modules: treat as browser.
  if (/^\s*['"]use client['"]\s*;?/m.test(text)) {
    return 'browser'
  }
  // Node/CommonJS patterns: treat as node.
  if (/\brequire\s*\(/.test(text) || /\bmodule\.exports\b/.test(text) || /\bexports\./.test(text) || /\bprocess\./.test(text)) {
    return 'node'
  }
  return 'both'
}

/**
 * @param {string} env - resolved env mode: browser|node|both
 * @returns {{ env: Record<string, boolean>, globals: Record<string, string> }}
 */
function eslintEnvAndGlobalsForLintEnv(env) {
  if (env === 'browser') {
    return { env: { browser: true, es2021: true }, globals: {} }
  }
  if (env === 'node') {
    return { env: { node: true, es2021: true }, globals: {} }
  }
  // both
  return { env: { browser: true, node: true, es2021: true }, globals: {} }
}

async function lintCode(code, filePath, opts = {}) {
  const envMode = resolveLintEnv(code, opts.lintEnv)
  const envAndGlobals = eslintEnvAndGlobalsForLintEnv(envMode)
  const eslint = new ESLint({
    cwd: BILD_CLI_ROOT,
    useEslintrc: false,
    baseConfig: {
      env: envAndGlobals.env,
      globals: envAndGlobals.globals,
      extends: ['eslint:recommended'],
      parser: resolveFromCli('@babel/eslint-parser'),
      parserOptions: {
        requireConfigFile: false,
        ecmaVersion: 2021,
        sourceType: 'module',
        ecmaFeatures: { jsx: true },
        babelOptions: {
          presets: babelPresetsForFile(filePath),
        },
      },
      rules: eslintRulesForFile(filePath),
    },
  })

  const results = await eslint.lintText(code, { filePath })
  return results[0]
}

function validateTailwindClasses(code) {
  const issues = []
  const lines = code.split('\n')

  lines.forEach((line, index) => {
    const classNameMatches = line.match(/className=["']([^"']+)["']/g)
    if (classNameMatches) {
      classNameMatches.forEach((match) => {
        const classString = match.match(/["']([^"']+)["']/)[1]
        const classes = classString.split(/\s+/)
        
        classes.forEach((cls) => {
          if (cls && !cls.match(/^[a-z]+:?[a-z-]*$/i) && !cls.startsWith('[')) {
            // Check for invalid Tailwind patterns
            if (!cls.match(/^(?:[a-z]+:)?(?:-?[a-z]+(?:-[a-z]+)*|\[[^\]]+\])$/i)) {
              issues.push({
                line: index + 1,
                message: `Potentially invalid Tailwind class: ${cls}`,
                severity: 'warning',
              })
            }
          }
        })
      })
    }
  })

  return issues
}

function validateTemplateSyntax(code) {
  const issues = []
  const lines = code.split('\n')

  // Accepts $(name:Type), $(name:Type="default"), $(name:Type='default'),
  // $(name:Type=default), $(name:Type[Option|value,Option 2|value2]="default"),
  // optionally followed by ;required=true|false.
  // Brackets may contain commas, pipes, spaces, hashes, hyphens, underscores, dots, slashes, $, parens.
  const VALID_RE = /^\$\([a-zA-Z_$][a-zA-Z0-9_$]*:[A-Za-z]+(?:\[[^\]]*\])?(?:=(?:"[^"]*"|'[^']*'|[^);]*))?(?:;[^)]*)?\)$/

  lines.forEach((line, index) => {
    const templateVars = line.match(TEMPLATE_VAR_REGEX)
    if (templateVars) {
      templateVars.forEach((varMatch) => {
        if (!VALID_RE.test(varMatch)) {
          issues.push({
            line: index + 1,
            message: `Invalid template variable syntax: ${varMatch}`,
            severity: 'error',
          })
        }
      })
    }
  })

  return issues
}

async function lint(code, filePath, opts = {}) {
  const issues = []
  
  // ESLint validation
  try {
    const eslintResult = await lintCode(code, filePath, opts)
    if (eslintResult.messages.length > 0) {
      issues.push(...eslintResult.messages.map((msg) => ({
        line: msg.line,
        column: msg.column,
        message: msg.message,
        severity: msg.severity === 2 ? 'error' : 'warning',
        rule: msg.ruleId,
      })))
    }
  } catch (error) {
    issues.push({
      line: 0,
      message: `ESLint error: ${error.message}`,
      severity: 'error',
    })
  }

  // Tailwind validation
  const tailwindIssues = validateTailwindClasses(code)
  issues.push(...tailwindIssues)

  // Template syntax validation
  const templateIssues = validateTemplateSyntax(code)
  issues.push(...templateIssues)

  return {
    issues,
    errorCount: issues.filter((i) => i.severity === 'error').length,
    warningCount: issues.filter((i) => i.severity === 'warning').length,
  }
}

module.exports = {
  lint,
  validateTailwindClasses,
  validateTemplateSyntax,
}

