const fs = require('fs')
const path = require('path')
const chalk = require('chalk')
const { getConfig } = require('../lib/config')
const {
  writeCmsDependenciesBoilerplate,
  writeCmsDependenciesBoilerplateToFilePath,
  writeComponentsBarrelBoilerplate,
} = require('../lib/cms-dependencies-boilerplate')
const {
  applyLibraryDepsRewrite,
  printLibraryDepsHints,
  analyzeLibraryDeps,
} = require('../lib/library-deps-hint')
const { applyDepsFileUpdates, depsReportHasPatch } = require('./template-from-jsx-shared')

function expandInputPathsToFiles(inputPaths) {
  const out = []
  for (const raw of inputPaths) {
    const resolvedPath = path.resolve(raw)
    if (!fs.existsSync(resolvedPath)) {
      throw new Error(`Path does not exist: ${raw}`)
    }
    const st = fs.statSync(resolvedPath)
    if (st.isDirectory()) {
      fs.readdirSync(resolvedPath).forEach((f) => {
        const full = path.join(resolvedPath, f)
        if (fs.statSync(full).isFile() && /\.(js|jsx|ts|tsx)$/i.test(f)) {
          out.push(full)
        }
      })
    } else {
      out.push(resolvedPath)
    }
  }
  return out
}

function boilerplateOptsFromCli(options, sitePath) {
  const withComponents = options.withoutComponents ? false : options.withComponents ? true : undefined
  return {
    force: options.force,
    withComponents,
    componentsImportPath: options.componentsImport,
    componentsBarrelRel: options.componentsBarrel,
    useJsxBarrel: options.jsx,
    createComponentsBarrel: options.createComponentsBarrel,
    sitePathForBarrel: sitePath,
  }
}

async function depsInitCommand(options) {
  const config = getConfig()
  const sitePath = options.sitePath || config.sitePath
  const relFromConfig = config.cmsDependenciesFile || 'src/cmsDependencies.ts'
  const bOpts = boilerplateOptsFromCli(options, sitePath)
  if (options.depsFile) {
    if (path.isAbsolute(options.depsFile)) {
      const r = writeCmsDependenciesBoilerplateToFilePath(options.depsFile, bOpts)
      return finishBoilerplateResult(r)
    }
    if (sitePath) {
      const r = writeCmsDependenciesBoilerplate(sitePath, options.depsFile, bOpts)
      return finishBoilerplateResult(r)
    }
    const r = writeCmsDependenciesBoilerplateToFilePath(path.resolve(options.depsFile), bOpts)
    return finishBoilerplateResult(r)
  }
  if (sitePath) {
    const r = writeCmsDependenciesBoilerplate(sitePath, relFromConfig, bOpts)
    return finishBoilerplateResult(r)
  }
  console.error(
    chalk.red(
      'No target: set `sitePath` in ~/.bild.json, or pass --site-path, or an explicit --deps-file path.',
    ),
  )
  process.exit(1)
}

function finishBoilerplateResult(result) {
  if (!result.ok) {
    console.error(chalk.red(`${result.path}: ${result.reason || 'failed'}`))
    process.exit(1)
  }
  if (result.skipped) {
    console.log(chalk.dim(`cmsDependencies already exists (use --force to replace): ${result.path}`))
  } else {
    console.log(chalk.green(`Created cmsDependencies boilerplate: ${result.path}`))
    if (result.withComponents) {
      console.log(
        chalk.dim(
          "Included @components (import * as Components + '@components' in the registry). Ensure tsconfig paths include the Components barrel.",
        ),
      )
    }
  }
}

async function depsComponentsCommand(options) {
  const config = getConfig()
  const sitePath = options.sitePath || config.sitePath
  if (!sitePath) {
    console.error(chalk.red('Pass --site-path or set `sitePath` in ~/.bild.json.'))
    process.exit(1)
  }
  const r = writeComponentsBarrelBoilerplate(sitePath, options.componentsBarrel, {
    force: options.force,
    useJsx: options.jsx,
  })
  if (!r.ok) {
    console.error(chalk.red(`${r.path}: ${r.reason || 'failed'}`))
    process.exit(1)
  }
  if (r.skipped) {
    console.log(chalk.dim(`Components barrel already exists (use --force to replace): ${r.path}`))
  } else {
    console.log(chalk.green(`Created components barrel: ${r.path}`))
  }
}

async function depsAddCommand(files, options) {
  const config = getConfig()
  if (!files || files.length === 0) {
    console.error(chalk.red('Pass one or more component files (or a folder of .js/.jsx/.ts/.tsx).'))
    process.exit(1)
  }
  let filePaths
  try {
    filePaths = expandInputPathsToFiles(files)
  } catch (e) {
    console.error(chalk.red(e.message))
    process.exit(1)
  }
  if (filePaths.length === 0) {
    console.error(chalk.red('No .js/.jsx/.ts/.tsx files found in the given paths.'))
    process.exit(1)
  }

  const sitePath = options.sitePath || config.sitePath
  const depsImportAlias = options.depsAlias || config.cmsDependenciesImportAlias || '@/cmsDependencies'
  const opt = {
    depsFile: options.depsFile,
    sitePath,
    depsImportAlias,
  }
  for (const filePath of filePaths) {
    if (options.dryRun) {
      const analysis = analyzeLibraryDeps(filePath, opt)
      if (analysis) {
        printLibraryDepsHints(analysis, filePath)
      }
      continue
    }
    const rewritten = applyLibraryDepsRewrite(filePath, opt)
    if (!rewritten.depsFilePath) {
      console.log(
        chalk.yellow(
          `Skipping ${filePath} — set sitePath in ~/.bild.json or use --site-path / --deps-file so the cmsDependencies file is known.`,
        ),
      )
      continue
    }
    printLibraryDepsHints(
      { report: rewritten.report, depsFilePath: rewritten.depsFilePath, depsImportAlias },
      filePath,
    )
    if (
      rewritten.report &&
      (depsReportHasPatch(rewritten.report) ||
        (rewritten.report.useBarrelReexports && rewritten.report.barrelReexportLines?.length))
    ) {
      await applyDepsFileUpdates(rewritten.depsFilePath, rewritten.report, 'deps add')
      console.log(chalk.dim(`Updated ${rewritten.depsFilePath}`))
    } else {
      console.log(
        chalk.dim(`No cmsDependencies file changes for ${filePath} (see hints for conflicts or nothing to extract).`),
      )
    }
  }
}

module.exports = {
  depsInitCommand,
  depsComponentsCommand,
  depsAddCommand,
}
