import fs from 'fs'
import path from 'path'
import readline from 'readline'
import { spawn } from 'child_process'

const NO_COPY = [
    'node_modules',
    'dist',
    'package-lock.json',
    '.git',
    'config.js',
]

const templateDir = path.join(process.cwd(), 'template')
const dropsDir = path.join(process.cwd(), 'drops')

const kebabCase = (str) =>
    str
        .replace(/([a-z])([A-Z])/g, '$1-$2')
        .replace(/[\s_]+/g, '-')
        .toLowerCase()

const pascalCase = (str) => {
    const camel = str.replace(/[-_\s]+(.)?/g, (_, c) =>
        c ? c.toUpperCase() : '',
    )
    return camel.charAt(0).toUpperCase() + camel.slice(1)
}
const titleCase = (str) => {
    const words = str.split('-')
    return words
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join(' ')
}

const copyDir = (src, dest) => {
    fs.mkdirSync(dest, { recursive: true })
    const entries = fs.readdirSync(src, { withFileTypes: true })

    for (const entry of entries) {
        const srcPath = path.join(src, entry.name)
        const destPath = path.join(dest, entry.name)

        if (NO_COPY.includes(entry.name)) continue

        if (entry.isDirectory()) {
            copyDir(srcPath, destPath)
        } else {
            fs.copyFileSync(srcPath, destPath)
        }
    }
}

const updatePackageJson = (dir, name) => {
    const pkgPath = path.join(dir, 'package.json')
    const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
    pkg.name = name
    fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
}

const replaceFileString = (
    dir,
    filePath,
    oldString,
    newString,
    newFilePath,
) => {
    const fullPath = path.join(dir, filePath)
    const fileContent = fs.readFileSync(fullPath, 'utf-8')
    const updatedContent = fileContent.replace(oldString, newString)

    if (newFilePath) {
        fs.writeFileSync(path.join(dir, newFilePath), updatedContent)
    } else {
        fs.writeFileSync(fullPath, updatedContent)
    }
}

const updateViteConfig = (dir, name) => {
    replaceFileString(dir, 'vite.config.js', /\$NAME/g, name)
}

const updateVue = (dir, kebabName, pascalName) => {
    replaceFileString(dir, 'src/App.vue', /Name/g, pascalName)
    replaceFileString(
        dir,
        'src/components/Name.vue',
        /\$name/g,
        kebabName,
        `src/components/${pascalName}.vue`,
    )

    fs.unlinkSync(path.join(dir, 'src/components/Name.vue'))
}

const updateIndexHtml = (dir, name) => {
    replaceFileString(dir, 'index.html', /\$NAME/g, name)
}

const updateMetaJson = (dir, kebabName, titleName) => {
    const metaPath = path.join(dir, 'public/meta.json')
    if (fs.existsSync(metaPath)) {
        let content = fs.readFileSync(metaPath, 'utf-8')
        content = content.replace(/\$name/g, kebabName)
        content = content.replace(/\$NAME/g, titleName)

        // Update the date to today's date
        const today = new Date().toISOString().split('T')[0]
        content = content.replace(/"date":\s*"[^"]*"/, `"date": "${today}"`)

        fs.writeFileSync(metaPath, content)
    }
}

const runCommand = (command, args, cwd = process.cwd()) => {
    return new Promise((resolve, reject) => {
        const child = spawn(command, args, {
            cwd,
            stdio: 'inherit',
            shell: true,
        })
        child.on('close', (code) => {
            if (code === 0) resolve()
            else reject(new Error(`Command failed with code ${code}`))
        })
        child.on('error', reject)
    })
}

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
})

rl.question('Enter drop name: ', async (name) => {
    const trimmedName = name.trim()
    const kebabName = kebabCase(trimmedName)
    const pascalName = pascalCase(trimmedName)
    const titleName = titleCase(kebabName)

    if (!kebabName) {
        console.error('Error: drop name cannot be empty')
        rl.close()
        process.exit(1)
    }

    const destDir = path.join(dropsDir, kebabName)

    if (fs.existsSync(destDir)) {
        console.error(`Error: drop "${kebabName}" already exists`)
        rl.close()
        process.exit(1)
    }

    try {
        console.log(`Creating drop "${kebabName}"...`)
        copyDir(templateDir, destDir)

        // Update template files
        updatePackageJson(destDir, kebabName)
        updateViteConfig(destDir, kebabName)
        updateVue(destDir, kebabName, pascalName)
        updateIndexHtml(destDir, titleName)
        updateMetaJson(destDir, kebabName, titleName)
        console.log(`Drop "${kebabName}" created successfully`)

        console.log('Running npm install...')
        await runCommand('npm', ['install'])
        console.log('npm install complete!')

        await runCommand('npm', ['run dev:drops', kebabName])
    } catch (err) {
        console.error('Error:', err.message)
        process.exit(1)
    }

    rl.close()
})
