import { promises as fs } from 'node:fs'
import path from 'node:path'

const componentsRoot = path.resolve(process.cwd(), 'app/components')
const storyExtensions = ['.stories.ts', '.stories.js', '.stories.tsx', '.stories.jsx']

const titleCase = (str) =>
  str
    .split(/[-_/]+/)
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join('/')

const toIdentifier = (str) =>
  str
    .split(/[^a-zA-Z0-9]+/)
    .filter(Boolean)
    .map((part, idx) => {
      const lower = part.toLowerCase()
      if (idx === 0) return lower.charAt(0).toUpperCase() + lower.slice(1)
      return part.charAt(0).toUpperCase() + part.slice(1)
    })
    .join('') || 'Component'

const hasStorySibling = async (dir, baseName) => {
  const files = await fs.readdir(dir)
  return files.some((file) => {
    const match = file.match(/^(.+)\.stories\.(t|j)sx?$/i)
    if (!match) return false
    return match[1] === baseName
  })
}

const walkVueFiles = async (dir) => {
  const entries = await fs.readdir(dir, { withFileTypes: true })
  const files = []

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name)
    if (entry.isDirectory()) {
      files.push(...(await walkVueFiles(fullPath)))
      continue
    }
    if (entry.isFile() && entry.name.endsWith('.vue')) {
      files.push(fullPath)
    }
  }
  return files
}

const buildStoryContent = (filePath) => {
  const baseName = path.basename(filePath, '.vue')
  const importPath = `./${path.basename(filePath)}`
  const relative = path.relative(componentsRoot, filePath).replace(/\\/g, '/')
  const title = titleCase(relative.replace('.vue', ''))
  const componentId = toIdentifier(baseName)

  return `import type { Meta, StoryObj } from '@storybook/vue3'
import StoryComponent from '${importPath}'

const meta: Meta<typeof StoryComponent> = {
  title: '${title}',
  component: StoryComponent,
  render: (args) => ({
    components: { StoryComponent },
    setup() {
      return { args }
    },
    template: '<StoryComponent v-bind=\"args\" />'
  })
}

export default meta
type Story = StoryObj<typeof meta>

export const Primary: Story = {
  args: {}
}
`
}

const ensureDir = async (dir) => {
  await fs.mkdir(dir, { recursive: true })
}

const run = async () => {
  const vueFiles = await walkVueFiles(componentsRoot)
  const generated = []

  for (const filePath of vueFiles) {
    const dir = path.dirname(filePath)
    const baseName = path.basename(filePath, '.vue')
    const storyExists = await hasStorySibling(dir, baseName)
    if (storyExists) continue

    const storyPath = path.join(dir, `${baseName}.stories.ts`)
    await ensureDir(dir)
    const content = buildStoryContent(filePath)
    await fs.writeFile(storyPath, content, 'utf8')
    generated.push(path.relative(process.cwd(), storyPath))
  }

  if (generated.length === 0) {
    console.log('No new stories generated. All components already have stories.')
    return
  }

  console.log(`Generated ${generated.length} stories:`)
  generated.forEach((p) => console.log(`- ${p}`))
}

run().catch((err) => {
  console.error('Failed to generate stories', err)
  process.exit(1)
})
